Пример #1
0
/*
 * Submit our certificate to the CA.
 * This, upon success, will return the signed CA.
 */
static int
docert(struct conn *c, const char *addr, const char *cert)
{
	char	*req;
	int	 rc;
	long	 lc;

	rc = 0;
	dodbg("%s: certificate", addr);

	if (NULL == (req = json_fmt_newcert(cert)))
		warnx("json_fmt_newcert");
	else if ((lc = sreq(c, addr, req)) < 0)
		warnx("%s: bad comm", addr);
	else if (200 != lc && 201 != lc)
		warnx("%s: bad HTTP: %ld", addr, lc);
	else if (0 == c->buf.sz || NULL == c->buf.buf)
		warnx("%s: empty response", addr);
	else
		rc = 1;

	if (0 == rc || verbose > 1)
		buf_dump(&c->buf);
	free(req);
	return(rc);
}
Пример #2
0
/*
 * Make sure that the given process exits properly, i.e., properly
 * exiting with EXIT_SUCCESS *or* 2.
 * Returns non-zero on success and zero on failure and sets the "rc"
 * value to be the exit status.
 */
int
checkexit_ext(int *rc, pid_t pid, enum comp comp)
{
	int		 c;
	const char	*cp;

	*rc = EXIT_FAILURE;

	if (-1 == waitpid(pid, &c, 0)) {
		warn("waitpid");
		return (0);
	}

	if ( ! WIFEXITED(c) && WIFSIGNALED(c)) {
		cp = strsignal(WTERMSIG(c));
		warnx("signal: %s(%u): %s", comps[comp], pid, cp);
		return (0);
	} else if ( ! WIFEXITED(c)) {
		warnx("did not exit: %s(%u)", comps[comp], pid);
		return (0);
	}

	/* Now check extended status. */

	if (EXIT_SUCCESS != (*rc = WEXITSTATUS(c)) && 2 != *rc) {
		dodbg("bad exit: %s(%u): %d", comps[comp], pid, *rc);
		return (0);
	}
	return (1);
}
Пример #3
0
/*
 * Look up directories from the certificate authority.
 */
static int
dodirs(struct conn *c, const char *addr, struct capaths *paths)
{
	struct jsmnn	*j;
	long		 lc;
	int		 rc;

	j = NULL;
	rc = 0;
	dodbg("%s: directories", addr);

	if ((lc = nreq(c, addr)) < 0)
		warnx("%s: bad comm", addr);
	else if (200 != lc && 201 != lc)
		warnx("%s: bad HTTP: %ld", addr, lc);
	else if (NULL == (j = json_parse(c->buf.buf, c->buf.sz)))
		warnx("json_parse");
	else if ( ! json_parse_capaths(j, paths))
		warnx("%s: bad CA paths", addr);
	else
		rc = 1;

	if (0 == rc || verbose > 1)
		buf_dump(&c->buf);
	json_free(j);
	return(rc);
}
Пример #4
0
static int
dorevoke(struct conn *c, const char *addr, const char *cert)
{
	char		*req;
	int		 rc;
	long		 lc;

	lc = 0;
	rc = 0;
	dodbg("%s: revocation", addr);

	if (NULL == (req = json_fmt_revokecert(cert)))
		warnx("json_fmt_revokecert");
	else if ((lc = sreq(c, addr, req)) < 0)
		warnx("%s: bad comm", addr);
	else if (200 != lc && 201 != lc && 409 != lc)
		warnx("%s: bad HTTP: %ld", addr, lc);
	else
		rc = 1;

	if (409 == lc)
		warnx("%s: already revoked", addr);

	if (0 == rc || verbose > 1)
		buf_dump(&c->buf);
	free(req);
	return(rc);
}
Пример #5
0
/*
 * Check with the CA whether a challenge has been processed.
 * Note: we'll only do this a limited number of times, and pause for a
 * time between checks, but this happens in the caller.
 */
static int
dochngcheck(struct conn *c, struct chng *chng)
{
	int		 cc;
	long		 lc;
	struct jsmnn	*j;

	dodbg("%s: status", chng->uri);

	if ((lc = nreq(c, chng->uri)) < 0) {
		warnx("%s: bad comm", chng->uri);
		return(0);
	} else if (200 != lc && 201 != lc && 202 != lc) {
		warnx("%s: bad HTTP: %ld", chng->uri, lc);
		buf_dump(&c->buf);
		return(0);
	} else if (NULL == (j = json_parse(c->buf.buf, c->buf.sz))) {
		warnx("%s: bad JSON object", chng->uri);
		buf_dump(&c->buf);
		return(0);
	} else if (-1 == (cc = json_parse_response(j))) {
		warnx("%s: bad response", chng->uri);
		buf_dump(&c->buf);
		json_free(j);
		return(0);
	} else if (cc > 0)
		chng->status = 1;

	json_free(j);
	return(1);
}
Пример #6
0
/*
 * Request a challenge for the given domain name.
 * This must happen for each name "alt".
 * On non-zero exit, fills in "chng" with the challenge.
 */
static int
dochngreq(struct conn *c, const char *alt,
	struct chng *chng, const struct capaths *p)
{
	int		 rc;
	char		*req;
	long		 lc;
	struct jsmnn	*j;

	j = NULL;
	rc = 0;
	dodbg("%s: req-auth: %s", p->newauthz, alt);

	if (NULL == (req = json_fmt_newauthz(alt)))
		warnx("json_fmt_newauthz");
	else if ((lc = sreq(c, p->newauthz, req)) < 0)
		warnx("%s: bad comm", p->newauthz);
	else if (200 != lc && 201 != lc)
		warnx("%s: bad HTTP: %ld", p->newauthz, lc);
	else if (NULL == (j = json_parse(c->buf.buf, c->buf.sz)))
		warnx("%s: bad JSON object", p->newauthz);
	else if ( ! json_parse_challenge(j, chng))
		warnx("%s: bad challenge", p->newauthz);
	else
		rc = 1;

	if (0 == rc || verbose > 1)
		buf_dump(&c->buf);
	json_free(j);
	free(req);
	return(rc);
}
Пример #7
0
/*
 * Send to the CA that we want to authorise a new account.
 * This only happens once for a new account key.
 * Returns non-zero on success.
 */
static int
donewreg(struct conn *c, const struct capaths *p)
{
	int		 rc;
	char		*req;
	long		 lc;

	rc = 0;
	dodbg("%s: new-reg", p->newreg);

	if (NULL == (req = json_fmt_newreg(URL_LICENSE)))
		warnx("json_fmt_newreg");
	else if ((lc = sreq(c, p->newreg, req)) < 0)
		warnx("%s: bad comm", p->newreg);
	else if (200 != lc && 201 != lc)
		warnx("%s: bad HTTP: %ld", p->newreg, lc);
	else if (NULL == c->buf.buf || 0 == c->buf.sz)
		warnx("%s: empty response", p->newreg);
	else
		rc = 1;

	if (0 == rc || verbose > 1)
		buf_dump(&c->buf);
	free(req);
	return(rc);
}
Пример #8
0
/*
 * Copyright (c) 2003, 2004 Henning Brauer <*****@*****.**>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
static ssize_t
host_dns(const char *s, struct addr *vec)
{
	struct addrinfo		 hints, *res0, *res;
	int			 error;
	ssize_t			 vecsz;
	struct sockaddr		*sa;

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
	/* ntpd MUST NOT use AI_ADDRCONFIG here */

	error = getaddrinfo(s, NULL, &hints, &res0);

	if (error == EAI_AGAIN || 
		/* FIXME */
#ifndef __FreeBSD__
	    error == EAI_NODATA || 
#endif
	    error == EAI_NONAME)
		return(0);

	if (error) {
		warnx("%s: parse error: %s", 
			s, gai_strerror(error));
		return(-1);
	}

	for (vecsz = 0, res = res0; 
	     NULL != res && vecsz < MAX_SERVERS_DNS; 
	     res = res->ai_next) {
		if (res->ai_family != AF_INET &&
		    res->ai_family != AF_INET6)
			continue;

		sa = res->ai_addr;

		if (AF_INET == res->ai_family) {
			vec[vecsz].family = 4;
			inet_ntop(AF_INET, 
				&(((struct sockaddr_in *)sa)->sin_addr), 
				vec[vecsz].ip, INET6_ADDRSTRLEN);
		} else {
			vec[vecsz].family = 6;
			inet_ntop(AF_INET6, 
				&(((struct sockaddr_in6 *)sa)->sin6_addr), 
				vec[vecsz].ip, INET6_ADDRSTRLEN);
		}

		dodbg("%s: DNS: %s", s, vec[vecsz].ip);
		vecsz++;
		break;
	}

	freeaddrinfo(res0);
	return(vecsz);
}
Пример #9
0
/*
 * Request the full chain certificate.
 */
static int
dofullchain(struct conn *c, const char *addr)
{
	int	 rc;
	long	 lc;

	rc = 0;
	dodbg("%s: full chain", addr);

	if ((lc = nreq(c, addr)) < 0)
		warnx("%s: bad comm", addr);
	else if (200 != lc && 201 != lc)
		warnx("%s: bad HTTP: %ld", addr, lc);
	else
		rc = 1;

	if (0 == rc || verbose > 1)
		buf_dump(&c->buf);
	return(rc);
}
Пример #10
0
/*
 * Make sure that the given process exits properly, i.e., properly
 * exiting with EXIT_SUCCESS.
 * Returns non-zero on success and zero on failure.
 */
int
checkexit(pid_t pid, enum comp comp)
{
	int		 c, cc;
	const char	*cp;

	if (-1 == waitpid(pid, &c, 0)) {
		warn("waitpid");
		return (0);
	} else if ( ! WIFEXITED(c) && WIFSIGNALED(c)) {
		cp = strsignal(WTERMSIG(c));
		warnx("signal: %s(%u): %s", comps[comp], pid, cp);
		return (0);
	} else if ( ! WIFEXITED(c)) {
		warnx("did not exit: %s(%u)", comps[comp], pid);
		return (0);
	} else if (EXIT_SUCCESS != WEXITSTATUS(c)) {
		cc = WEXITSTATUS(c);
		dodbg("bad exit: %s(%u): %d", comps[comp], pid, cc);
		return (0);
	}

	return (1);
}
Пример #11
0
/*
 * If something goes wrong (or we're tracing output), we dump the
 * current transfer's data as a debug message.
 * Make sure that print all non-printable characters as question marks
 * so that we don't spam the console.
 * Also, consolidate white-space.
 * This of course will ruin string literals, but the intent here is just
 * to show the message, not to replicate it.
 */
static void
buf_dump(const struct buf *buf)
{
	size_t	 i;
	int	 j;
	char	*nbuf;

	if (0 == buf->sz)
		return;
	if (NULL == (nbuf = malloc(buf->sz)))
		err(EXIT_FAILURE, "malloc");

	for (j = 0, i = 0; i < buf->sz; i++)
		if (isspace((int)buf->buf[i])) {
			nbuf[j++] = ' ';
			while (isspace((int)buf->buf[i]))
				i++;
			i--;
		} else
			nbuf[j++] = isprint((int)buf->buf[i]) ?
				buf->buf[i] : '?';
	dodbg("transfer buffer: [%.*s] (%zu bytes)", j, nbuf, buf->sz);
	free(nbuf);
}
Пример #12
0
/*
 * Note to the CA that a challenge response is in place.
 */
static int
dochngresp(struct conn *c, const struct chng *chng, const char *th)
{
	int	 rc;
	long	 lc;
	char	*req;

	rc = 0;
	dodbg("%s: challenge", chng->uri);

	if (NULL == (req = json_fmt_challenge(chng->token, th)))
		warnx("json_fmt_challenge");
	else if ((lc = sreq(c, chng->uri, req)) < 0)
		warnx("%s: bad comm", chng->uri);
	else if (200 != lc && 201 != lc && 202 != lc)
		warnx("%s: bad HTTP: %ld", chng->uri, lc);
	else
		rc = 1;

	if (0 == rc || verbose > 1)
		buf_dump(&c->buf);
	free(req);
	return(rc);
}
Пример #13
0
int
fileproc(int certsock, const char *certdir, const struct config *cfg)
{
	char		*csr = NULL, *ch = NULL;
	char		 file[PATH_MAX];
	size_t		 chsz, csz;
	int		 rc = 0;
	long		 lval;
	enum fileop	 op;
	time_t		 t;

	/* File-system and sandbox jailing. */

	if ( ! sandbox_before())
		goto out;
	else if ( ! dropfs(certdir))
		goto out;
	else if ( ! sandbox_after(0))
		goto out;

	/* Read our operation. */

	op = FILE__MAX;
	if (0 == (lval = readop(certsock, COMM_CHAIN_OP)))
		op = FILE_STOP;
	else if (FILE_CREATE == lval || FILE_REMOVE == lval)
		op = lval;

	if (FILE_STOP == op) {
		rc = 1;
		goto out;
	} else if (FILE__MAX == op) {
		warnx("unknown operation from certproc");
		goto out;
	}

	/*
	 * If we're backing up, then copy all files (found) by linking
	 * them to the file followed by the epoch in seconds.
	 * If we're going to remove, the unlink(2) will cause the
	 * original to go away.
	 * If we're going to update, the rename(2) will replace the
	 * certificate, leaving the backup as the only one.
	 */

	if (cfg->backup) {
		t = time(NULL);
		snprintf(file, sizeof(file),
			"cert-%llu.pem", (unsigned long long)t);
		if (-1 == link(CERT_PEM, file) && ENOENT != errno) {
			warnx("%s/%s", certdir, CERT_PEM);
			goto out;
		} else
			dodbg("%s/%s: linked to %s",
				certdir, CERT_PEM, file);

		snprintf(file, sizeof(file),
			"chain-%llu.pem", (unsigned long long)t);
		if (-1 == link(CHAIN_PEM, file) && ENOENT != errno) {
			warnx("%s/%s", certdir, CHAIN_PEM);
			goto out;
		} else
			dodbg("%s/%s: linked to %s",
				certdir, CHAIN_PEM, file);

		snprintf(file, sizeof(file),
			"fullchain-%llu.pem", (unsigned long long)t);
		if (-1 == link(FCHAIN_PEM, file) && ENOENT != errno) {
			warnx("%s/%s", certdir, FCHAIN_PEM);
			goto out;
		} else
			dodbg("%s/%s: linked to %s",
				certdir, FCHAIN_PEM, file);
	}

	/*
	 * If revoking certificates, just unlink the files.
	 * We return the special error code of 2 to indicate that the
	 * certificates were removed.
	 */

	if (FILE_REMOVE == op) {
		if (-1 == unlink(CERT_PEM) && ENOENT != errno) {
			warn("%s/%s", certdir, CERT_PEM);
			goto out;
		} else
			dodbg("%s/%s: unlinked", certdir, CERT_PEM);

		if (-1 == unlink(CHAIN_PEM) && ENOENT != errno) {
			warn("%s/%s", certdir, CHAIN_PEM);
			goto out;
		} else
			dodbg("%s/%s: unlinked", certdir, CHAIN_PEM);

		if (-1 == unlink(FCHAIN_PEM) && ENOENT != errno) {
			warn("%s/%s", certdir, FCHAIN_PEM);
			goto out;
		} else
			dodbg("%s/%s: unlinked", certdir, FCHAIN_PEM);

		rc = 2;
		goto out;
	}

	/*
	 * Start by downloading the chain PEM as a buffer.
	 * This is not nil-terminated, but we're just going to guess
	 * that it's well-formed and not actually touch the data.
	 * Once downloaded, dump it into CHAIN_BAK.
	 */

	if (NULL == (ch = readbuf(certsock, COMM_CHAIN, &chsz)))
		goto out;
	if ( ! serialise(CHAIN_BAK, CHAIN_PEM, ch, chsz, NULL, 0))
		goto out;

	dodbg("%s/%s: created", certdir, CHAIN_PEM);

	/*
	 * Next, wait until we receive the DER encoded (signed)
	 * certificate from the network process.
	 * This comes as a stream of bytes: we don't know how many, so
	 * just keep downloading.
	 */

	if (NULL == (csr = readbuf(certsock, COMM_CSR, &csz)))
		goto out;
	if ( ! serialise(CERT_BAK, CERT_PEM, csr, csz, NULL, 0))
		goto out;

	dodbg("%s/%s: created", certdir, CERT_PEM);

	/*
	 * Finally, create the full-chain file.
	 * This is just the concatenation of the certificate and chain.
	 * We return the special error code 2 to indicate that the
	 * on-file certificates were changed.
	 */

	if ( ! serialise(FCHAIN_BAK, FCHAIN_PEM, csr, csz, ch, chsz))
		goto out;

	dodbg("%s/%s: created", certdir, FCHAIN_PEM);

	rc = 2;
out:
	close(certsock);
	free(csr);
	free(ch);
	return (rc);
}
Пример #14
0
int
chngproc(int netsock, const char *root, int remote)
{
	int		  rc;
	long		  lval;
	enum chngop	  op;
	char		 *tok, *th, *fmt;
	char		**fs;
	size_t		  i, fsz;
	void		 *pp;
	int		  fd, cc;

	rc = 0;
	th = tok = fmt = NULL;
	fd = -1;
	fs = NULL;
	fsz = 0;

	/* File-system and sandbox jailing. */

	if ( ! sandbox_before())
		goto out;
	else if ( ! dropfs(root))
		goto out;
	else if ( ! sandbox_after())
		goto out;

	/* 
	 * Loop while we wait to get a thumbprint and token.
	 * We'll get this for each SAN request.
	 */

	for (;;) {
		op = CHNG__MAX;
		if (0 == (lval = readop(netsock, COMM_CHNG_OP))) 
			op = CHNG_STOP;
		else if (CHNG_SYN == lval)
			op = lval;

		if (CHNG__MAX == op) {
			warnx("unknown operation from netproc");
			goto out;
		} else if (CHNG_STOP == op)
			break;

		assert(CHNG_SYN == op);

		/* 
		 * Read the thumbprint and token.
		 * The token is the filename, so store that in a vector
		 * of tokens that we'll later clean up.
		 */

		if (NULL == (th = readstr(netsock, COMM_THUMB)))
			goto out;
		else if (NULL == (tok = readstr(netsock, COMM_TOK)))
			goto out;

		/* Vector appending... */

		pp = realloc(fs, (fsz + 1) * sizeof(char *));
		if (NULL == pp) {
			warn("realloc");
			goto out;
		}
		fs = pp;
		fs[fsz] = tok;
		tok = NULL;
		fsz++;

		if (-1 == asprintf(&fmt, "%s.%s", fs[fsz - 1], th)) {
			warn("asprintf");
			goto out;
		}

		/*
		 * I use this for testing when letskencrypt is being run
		 * on machines apart from where I'm hosting the
		 * challenge directory.
		 * DON'T DEPEND ON THIS FEATURE.
		 */
		if (remote) {
			puts("RUN THIS IN THE CHALLENGE DIRECTORY");
			puts("YOU HAVE 20 SECONDS...");
			printf("doas sh -c \"echo %s > %s\"\n", 
				fmt, fs[fsz - 1]);
			sleep(20);
			puts("TIME'S UP.");
		} else { 
			/* 
			 * Create and write to our challenge file.
			 * Note: we use file descriptors instead of FILE
			 * because we want to minimise our pledges.
			 */
			fd = open(fs[fsz - 1], 
				O_WRONLY|O_EXCL|O_CREAT, 0444);
			if (-1 == fd) {
				warn("%s", fs[fsz - 1]);
				goto out;
			} if (-1 == write(fd, fmt, strlen(fmt))) {
				warn("%s", fs[fsz - 1]);
				goto out;
			} else if (-1 == close(fd)) {
				warn("%s", fs[fsz - 1]);
				goto out;
			}
			fd = -1;
		}

		free(th);
		free(fmt);
		th = fmt = NULL;

		dodbg("%s/%s: created", root, fs[fsz - 1]);

		/* 
		 * Write our acknowledgement. 
		 * Ignore reader failure.
		 */

		cc = writeop(netsock, COMM_CHNG_ACK, CHNG_ACK);
		if (0 == cc)
			break;
		if (cc < 0)
			goto out;
	}

	rc = 1;
out:
	close(netsock);
	if (-1 != fd)
		close(fd);
	for (i = 0; i < fsz; i++) {
		if (-1 == unlink(fs[i]) && ENOENT != errno)
			warn("%s", fs[i]);
		free(fs[i]);
	}
	free(fs);
	free(fmt);
	free(th);
	free(tok);
	return(rc);
}