Example #1
0
/*
 * Contact dnsproc and resolve a host.
 * Place the answers in "v" and return the number of answers, which can
 * be at most MAX_SERVERS_DNS.
 * Return <0 on failure.
 */
static ssize_t
urlresolve(int fd, const char *host, struct source *v)
{
	char	  	  *addr;
	size_t	  	   i, sz;
	long	  	   lval;

	if (writeop(fd, COMM_DNS, DNS_LOOKUP) <= 0)
		return(-1);
	else if (writestr(fd, COMM_DNSQ, host) <= 0)
		return(-1);
	else if ((lval = readop(fd, COMM_DNSLEN)) < 0)
		return(-1);

	sz = lval;
	assert(sz <= MAX_SERVERS_DNS);

	for (i = 0; i < sz; i++) {
		memset(&v[i], 0, sizeof(struct source));
		if ((lval = readop(fd, COMM_DNSF)) < 0)
			goto err;
		else if (4 != lval && 6 != lval)
			goto err;
		else if (NULL == (addr = readstr(fd, COMM_DNSA)))
			goto err;
		v[i].family = lval;
		v[i].ip = addr;
	}

	return(sz);
err:
	for (i = 0; i < sz; i++)
		free(v[i].ip);
	return(-1);
}
Example #2
0
File: as23.c Project: sergev/bk0012
void assem()
{
	union token ttok;

	while(1) {
		readop();
		if(tok.u != TOKFILE && tok.u != '<') {
			if(checkeos())
				goto ealoop;
			ttok.u = tok.u;
			if(tok.u == TOKINT) {
				ttok.u = 2;
				agetw();
				numval = tok.u;
			}
			readop();
			switch(tok.u) {
			case '=':
				doequal(&ttok);
				goto ealoop;
			case ':':
				docolon(&ttok);
				continue;
			default:
				savop = tok.u;
				tok.u = ttok.u;
				break;
			}
		}

		opline();
		dotmax();

	ealoop:

		if(tok.u == '\n')
			++line;
		if(DEBUG)
			printf("\nLine %d: ",line);
		if(tok.u == TOKEOF)
			return;
	}
}
Example #3
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);
}
Example #4
0
static signed int read32(unsigned pc)
{
	return readop(pc) | (readop(pc+1) << 8)| (readop(pc+2) << 16)| (readop(pc+3) << 24);
}
Example #5
0
static signed short read16(unsigned pc)
{
	return readop(pc) | (readop(pc+1) << 8);
}
Example #6
0
static signed char read8(unsigned pc)
{
	return readop(pc);
}
Example #7
0
static int decode_AM(unsigned ipc, unsigned pc, int m, int opsize, char *out)
{
	unsigned char mod = readop(pc);
	if(m) {
		switch(mod>>5) {
		case 0: // Double displacement (8 bit)
			out_AM_DoubleDisplacement(mod&0x1F, read8(pc+1), read8(pc+2), opsize, out);
			return 3;

		case 1: // Double displacement (16 bit)
			out_AM_DoubleDisplacement(mod&0x1F, read16(pc+1), read16(pc+3), opsize, out);
			return 5;

		case 2: // Double displacement (32 bit)
			out_AM_DoubleDisplacement(mod&0x1F, read32(pc+1), read32(pc+5), opsize, out);
			return 9;

		case 3: // Register
			out_AM_Register(mod&0x1F, out);
			return 1;

		case 4: // Autoincrement
			out_AM_Autoincrement(mod&0x1F, opsize, out);
			return 1;

		case 5: // Autodecrement
			out_AM_Autodecrement(mod&0x1F, opsize, out);
			return 1;

		case 6:
			switch (readop(pc+1)>>5)
				{
				case 0: // Displacement indexed (8 bit)
					out_AM_DisplacementIndexed(readop(pc+1)&0x1F, mod&0x1F, read8(pc+2), opsize, out);
					return 3;

				case 1: // Displacement indexed (16 bit)
					out_AM_DisplacementIndexed(readop(pc+1)&0x1F, mod&0x1F, read16(pc+2), opsize, out);
					return 4;

				case 2: // Displacement indexed (32 bit)
					out_AM_DisplacementIndexed(readop(pc+1)&0x1F, mod&0x1F, read32(pc+2), opsize, out);
					return 6;

				case 3: // Register indirect indexed
					out_AM_RegisterIndirectIndexed(readop(pc+1)&0x1F, mod&0x1F, opsize, out);
					return 2;

				case 4: // Displacement indirect indexed (8 bit)
					out_AM_DisplacementIndirectIndexed(readop(pc+1)&0x1F, mod&0x1F, read8(pc+2), opsize, out);
					return 3;

				case 5: // Displacement indirect indexed (16 bit)
					out_AM_DisplacementIndirectIndexed(readop(pc+1)&0x1F, mod&0x1F, read16(pc+2), opsize, out);
					return 4;

				case 6: // Displacement indirect indexed (32 bit)
					out_AM_DisplacementIndirectIndexed(readop(pc+1)&0x1F, mod&0x1F, read32(pc+2), opsize, out);
					return 6;

				case 7:
					switch (readop(pc+1)&0x1F)
						{
						case 16: // PC Displacement Indexed (8 bit)
							out_AM_PCDisplacementIndexed(ipc, read8(pc+2), mod&0x1F, opsize, out);
							return 3;

						case 17: // PC Displacement Indexed (16 bit)
							out_AM_PCDisplacementIndexed(ipc, read16(pc+2), mod&0x1F, opsize, out);
							return 4;

						case 18: // PC Displacement Indexed (32 bit)
							out_AM_PCDisplacementIndexed(ipc, read32(pc+2), mod&0x1F, opsize, out);
							return 6;

						case 19: // Direct Address Indexed
							out_AM_DirectAddressIndexed(read32(pc+2), mod&0x1F, opsize, out);
							return 6;

						case 24: // PC Displacement Indirect Indexed(8 bit)
							out_AM_PCDisplacementIndirectIndexed(ipc, read8(pc+2), mod&0x1F, opsize, out);
							return 3;

						case 25: // PC Displacement Indirect Indexed (16 bit)
							out_AM_PCDisplacementIndirectIndexed(ipc, read16(pc+2), mod&0x1F, opsize, out);
							return 4;

						case 26: // PC Displacement Indirect Indexed (32 bit)
							out_AM_PCDisplacementIndirectIndexed(ipc, read32(pc+2), mod&0x1F, opsize, out);
							return 6;

						case 27: // Direct Address Deferred Indexed
							out_AM_DirectAddressDeferredIndexed(read32(pc+2), mod&0x1F, opsize, out);
							return 6;

						default:
							strcat(out, "!ERRAM3");
							return 1;
						}

				default:
					strcat(out, "!ERRAM2");
					return 1;
				}

		default:
			strcat(out, "!ERRAM1");
			return 1;
		}
	} else {
		switch(mod>>5) {
Example #8
0
int
dnsproc(int nfd)
{
	int		 rc, cc;
	char		*look, *last;
	struct addr	 v[MAX_SERVERS_DNS];
	long		 lval;
	size_t		 i;
	ssize_t		 vsz;
	enum dnsop	 op;

	rc = 0;
	look = last = NULL;
	vsz = 0;

	/*
	 * Why don't we chroot() here?
	 * On OpenBSD, the pledge(2) takes care of our constraining the
	 * environment to DNS resolution only, so the chroot(2) is
	 * unnecessary.
	 * On Mac OS X, we can't chroot(2): we'd need to have an mdns
	 * responder thing in each jail.
	 * On Linux, forget it.  getaddrinfo(2) pulls on all sorts of
	 * mystery meat.
	 */

	if ( ! sandbox_before())
		goto out;
	else if ( ! dropprivs())
		goto out;
	else if ( ! sandbox_after()) 
		goto out;

	/*
	 * This is simple: just loop on a request operation, and each
	 * time we write back zero or more entries.
	 * Also do a simple trick and cache the last lookup.
	 */

	for (;;) {
		op = DNS__MAX;
		if (0 == (lval = readop(nfd, COMM_DNS)))
			op = DNS_STOP;
		else if (DNS_LOOKUP == lval)
			op = lval;

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

		if (NULL == (look = readstr(nfd, COMM_DNSQ)))
			goto out;

		/* 
		 * Check if we're asked to repeat the lookup.
		 * If not, request it from host_dns().
		 */

		if (NULL == last || strcmp(look, last)) {
			if ((vsz = host_dns(look, v)) < 0)
				goto out;

			free(last);
			last = look;
			look = NULL;
		} else {
			doddbg("%s: cached", look);
			free(look);
			look = NULL;
		}

		if (0 == (cc = writeop(nfd, COMM_DNSLEN, vsz)))
			break;
		else if (cc < 0)
			goto out;
		for (i = 0; i < (size_t)vsz; i++) { 
			if (writeop(nfd, COMM_DNSF, v[i].family) <= 0)
				goto out;
			if (writestr(nfd, COMM_DNSA, v[i].ip) <= 0)
				goto out;
		}
	}

	rc = 1;
out:
	close(nfd);
	free(look);
	free(last);
	return(rc);
}
int
certproc(int netsock, int filesock)
{
	char		*csr, *chain, *url;
	unsigned char	*csrcp, *chaincp;
	size_t		 csrsz, chainsz;
	int		 i, rc, idx, cc;
	enum certop	 op;
	long		 lval;
	X509		*x, *chainx;
	X509_EXTENSION	*ext;
	X509V3_EXT_METHOD *method;
	void		*entries;
	STACK_OF(CONF_VALUE) *val;
	CONF_VALUE	*nval;

	ext = NULL;
	idx = -1;
	method = NULL;
	chain = csr = url = NULL;
	rc = 0;
	x = chainx = NULL;

	/* File-system and sandbox jailing. */

	if ( ! sandbox_before())
		goto out;

	ERR_load_crypto_strings();

	if ( ! dropfs(PATH_VAR_EMPTY))
		goto out;
	else if ( ! dropprivs())
		goto out;
	else if ( ! sandbox_after())
		goto out;

	/* Read what the netproc wants us to do. */

	op = CERT__MAX;
	if (0 == (lval = readop(netsock, COMM_CSR_OP)))
		op = CERT_STOP;
	else if (CERT_REVOKE == lval || CERT_UPDATE == lval)
		op = lval;

	if (CERT_STOP == op) {
		rc = 1;
		goto out;
	} else if (CERT__MAX == op) {
		warnx("unknown operation from netproc");
		goto out;
	}

	/* 
	 * Pass revocation right through to fileproc. 
	 * If the reader is terminated, ignore it.
	 */

	if (CERT_REVOKE == op) {
		if (writeop(filesock, COMM_CHAIN_OP, FILE_REMOVE) >= 0)
			rc = 1;
		goto out;
	}

	/*
	 * Wait until we receive the DER encoded (signed) certificate
	 * from the network process.
	 * Then convert the DER encoding into an X509 certificate.
	 */

	if (NULL == (csr = readbuf(netsock, COMM_CSR, &csrsz)))
		goto out;

	csrcp = (u_char *)csr;
	x = d2i_X509(NULL, (const u_char **)&csrcp, csrsz);
	if (NULL == x) {
		warnx("d2i_X509");
		goto out;
	}

	/*
	 * Extract the CA Issuers from its NID.
	 * TODO: I have no idea what I'm doing.
	 */

	idx = X509_get_ext_by_NID(x, NID_info_access, idx);
	if (idx >= 0 && NULL != (ext = X509_get_ext(x, idx)))
		method = (X509V3_EXT_METHOD *)X509V3_EXT_get(ext);

	entries = X509_get_ext_d2i(x, NID_info_access, 0, 0);
	if (NULL != method && NULL != entries) {
		val = method->i2v(method, entries, 0);
		for (i = 0; i < sk_CONF_VALUE_num(val); i++) {
			nval = sk_CONF_VALUE_value(val, i);
			if (strcmp(nval->name, "CA Issuers - URI"))
				continue;
			url = strdup(nval->value);
			if (NULL == url) {
				warn("strdup");
				goto out;
			}
			break;
		}
	}

	if (NULL == url) {
		warnx("no CA issuer registered with certificate");
		goto out;
	}

	/* Write the CA issuer to the netsock. */

	if (writestr(netsock, COMM_ISSUER, url) <= 0)
		goto out;

	/* Read the full-chain back from the netsock. */

	if (NULL == (chain = readbuf(netsock, COMM_CHAIN, &chainsz)))
		goto out;

	/*
	 * Then check if the chain is PEM-encoded by looking to see if
	 * it begins with the PEM marker.
	 * If so, ship it as-is; otherwise, convert to a PEM encoded
	 * buffer and ship that.
	 * FIXME: if PEM, re-parse it.
	 */

	if (chainsz <= strlen(MARKER) ||
	    strncmp(chain, MARKER, strlen(MARKER))) {
		chaincp = (u_char *)chain;
		chainx = d2i_X509(NULL, 
			(const u_char **)&chaincp, chainsz);
		if (NULL == chainx) {
			warnx("d2i_X509");
			goto out;
		}
		free(chain);
		if (NULL == (chain = x509buf(chainx, &chainsz)))
			goto out;
	} 

	/* Allow reader termination to just push us out. */

	if (0 == (cc = writeop(filesock, COMM_CHAIN_OP, FILE_CREATE)))
		rc = 1;
	if (cc <= 0)
		goto out;
	if (0 == (cc = writebuf(filesock, COMM_CHAIN, chain, chainsz)))
		rc = 1;
	if (cc <= 0)
		goto out;

	/* 
	 * Next, convert the X509 to a buffer and send that. 
	 * Reader failure doesn't change anything.
	 */

	free(chain);
	if (NULL == (chain = x509buf(x, &chainsz)))
		goto out;
	if (writebuf(filesock, COMM_CSR, chain, chainsz) < 0)
		goto out;

	rc = 1;
out:
	close(netsock);
	close(filesock);
	if (NULL != x)
		X509_free(x);
	if (NULL != chainx)
		X509_free(chainx);
	free(csr);
	free(url);
	free(chain);
	ERR_print_errors_fp(stderr);
	ERR_free_strings();
	return(rc);
}
Example #10
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);
}
Example #11
0
/*
 * Here we communicate with the letsencrypt server.
 * For this, we'll need the certificate we want to upload and our
 * account key information.
 */
int
netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd,
	int newacct, int revoke, int staging,
	const char *const *alts, size_t altsz)
{
	int		 rc;
	size_t		 i;
	char		*cert, *thumb, *url;
	struct conn	 c;
	struct capaths	 paths;
	struct chng 	*chngs;
	long		 lval;

	rc = 0;
	memset(&paths, 0, sizeof(struct capaths));
	memset(&c, 0, sizeof(struct conn));
	url = cert = thumb = NULL;
	chngs = NULL;

	/* File-system, user, and sandbox jail. */

	if ( ! sandbox_before())
		goto out;
	else if ( ! dropfs(PATH_VAR_EMPTY))
		goto out;
	else if ( ! dropprivs())
		goto out;
	else if ( ! sandbox_after())
		goto out;

	/*
	 * Wait until the acctproc, keyproc, and revokeproc have started
	 * up and are ready to serve us data.
	 * There's no point in running if these don't work.
	 * Then check whether revokeproc indicates that the certificate
	 * on file (if any) can be updated.
	 */

	if (0 == (lval = readop(afd, COMM_ACCT_STAT))) {
		rc = 1;
		goto out;
	} else if (ACCT_READY != lval) {
		warnx("unknown operation from acctproc");
		goto out;
	}

	if (0 == (lval = readop(kfd, COMM_KEY_STAT))) {
		rc = 1;
		goto out;
	} else if (KEY_READY != lval) {
		warnx("unknown operation from keyproc");
		goto out;
	}

	if (0 == (lval = readop(rfd, COMM_REVOKE_RESP))) {
		rc = 1;
		goto out;
	} else if (REVOKE_EXP != lval && REVOKE_OK != lval) {
		warnx("unknown operation from revokeproc");
		goto out;
	}

	/* If our certificate is up-to-date, return now. */

	if (REVOKE_OK == lval) {
		rc = 1;
		goto out;
	}

	/* Allocate main state. */

	chngs = calloc(altsz, sizeof(struct chng));
	if (NULL == chngs) {
		warn("calloc");
		goto out;
	}

	c.dfd = dfd;
	c.fd = afd;
	c.na = staging ? URL_STAGE_CA : URL_REAL_CA;

	/*
	 * Look up the domain of the ACME server.
	 * We'll use this ourselves instead of having libcurl do the DNS
	 * resolution itself.
	 */
	if ( ! dodirs(&c, c.na, &paths))
		goto out;

	/*
	 * If we're meant to revoke, then wait for revokeproc to send us
	 * the certificate (if it's found at all).
	 * Following that, submit the request to the CA then notify the
	 * certproc, which will in turn notify the fileproc.
	 */

	if (revoke) {
		if (NULL == (cert = readstr(rfd, COMM_CSR)))
			goto out;
		if ( ! dorevoke(&c, paths.revokecert, cert))
			goto out;
		else if (writeop(cfd, COMM_CSR_OP, CERT_REVOKE) > 0)
			rc = 1;
		goto out;
	}

	/* If new, register with the CA server. */

	if (newacct && ! donewreg(&c, &paths))
		goto out;

	/* Pre-authorise all domains with CA server. */

	for (i = 0; i < altsz; i++)
		if ( ! dochngreq(&c, alts[i], &chngs[i], &paths))
			goto out;

	/*
	 * We now have our challenges.
	 * We need to ask the acctproc for the thumbprint.
	 * We'll combine this to the challenge to create our response,
	 * which will be orchestrated by the chngproc.
	 */

	if (writeop(afd, COMM_ACCT, ACCT_THUMBPRINT) <= 0)
		goto out;
	else if (NULL == (thumb = readstr(afd, COMM_THUMB)))
		goto out;

	/* We'll now ask chngproc to build the challenge. */

	for (i = 0; i < altsz; i++) {
		if (writeop(Cfd, COMM_CHNG_OP, CHNG_SYN) <= 0)
			goto out;
		else if (writestr(Cfd, COMM_THUMB, thumb) <= 0)
			goto out;
		else if (writestr(Cfd, COMM_TOK, chngs[i].token) <= 0)
			goto out;

		/* Read that the challenge has been made. */

		if (CHNG_ACK != readop(Cfd, COMM_CHNG_ACK))
			goto out;

		/* Write to the CA that it's ready. */

		if ( ! dochngresp(&c, &chngs[i], thumb))
			goto out;
	}

	/*
	 * We now wait on the ACME server for each domain.
	 * Connect to the server (assume it's the same server) once
	 * every five seconds.
	 */

	for (i = 0; i < altsz; i++) {
		if (1 == chngs[i].status)
			continue;

		if (chngs[i].retry++ >= RETRY_MAX) {
			warnx("%s: too many tries", chngs[i].uri);
			goto out;
		}

		/* Sleep before every attempt. */
		sleep(RETRY_DELAY);
		if ( ! dochngcheck(&c, &chngs[i]))
			goto out;
	}

	/*
	 * Write our acknowledgement that the challenges are over.
	 * The challenge process will remove all of the files.
	 */

	if (writeop(Cfd, COMM_CHNG_OP, CHNG_STOP) <= 0)
		goto out;

	/* Wait to receive the certificate itself. */

	if (NULL == (cert = readstr(kfd, COMM_CERT)))
		goto out;

	/*
	 * Otherwise, submit the CA for signing, download the signed
	 * copy, and ship that into the certificate process for copying.
	 */

	if ( ! docert(&c, paths.newcert, cert))
		goto out;
	else if (writeop(cfd, COMM_CSR_OP, CERT_UPDATE) <= 0)
		goto out;
	else if (writebuf(cfd, COMM_CSR, c.buf.buf, c.buf.sz) <= 0)
		goto out;

	/*
	 * Read back the issuer from the certproc.
	 * Then contact the issuer to get the certificate chain.
	 * Write this chain directly back to the certproc.
	 */

	if (NULL == (url = readstr(cfd, COMM_ISSUER)))
		goto out;
	else if ( ! dofullchain(&c, url))
		goto out;
	else if (writebuf(cfd, COMM_CHAIN, c.buf.buf, c.buf.sz) <= 0)
		goto out;

	rc = 1;
out:
	close(cfd);
	close(kfd);
	close(afd);
	close(Cfd);
	close(dfd);
	close(rfd);
	free(cert);
	free(url);
	free(thumb);
	free(c.buf.buf);
	if (NULL != chngs)
		for (i = 0; i < altsz; i++)
			json_free_challenge(&chngs[i]);
	free(chngs);
	json_free_capaths(&paths);
	return(rc);
}