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
static void
rewind_tape(int fd)
{
	struct stat sp;

	if(fstat(fd, &sp))
		errx(12, "fstat in rewind");

	/*
	 * don't want to do tape ioctl on regular files:
	 */
	if( S_ISREG(sp.st_mode) ) {
		if( lseek(fd, 0, SEEK_SET) == -1 )
			errx(13, "lseek");
	} else
		/*  assume its a tape	*/
		writeop(fd, MTREW);
}
Example #3
0
int
main(int argc, char *argv[])
{
	int lastnread, nread, nw, inp, outp;
	enum {READ, VERIFY, COPY, COPYVERIFY} op = READ;
	sig_t oldsig;
	int ch, needeof;
	char *buff;
	const char *inf;

	msg = stdout;
	guesslen = 1;
	outp = -1;
	while ((ch = getopt(argc, argv, "cs:vx")) != -1)
		switch((char)ch) {
		case 'c':
			op = COPYVERIFY;
			break;
		case 's':
			maxblk = atoi(optarg);
			if (maxblk <= 0) {
				warnx("illegal block size");
				usage();
			}
			guesslen = 0;
			break;
		case 'v':
			op = VERIFY;
			break;
		case 'x':
			msg = stderr;
			break;
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	switch(argc) {
	case 0:
		if (op != READ)
			usage();
		inf = _PATH_DEFTAPE;
		break;
	case 1:
		if (op != READ)
			usage();
		inf = argv[0];
		break;
	case 2:
		if (op == READ)
			op = COPY;
		inf = argv[0];
		if ((outp = open(argv[1], op == VERIFY ? O_RDONLY :
		    op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0)
			err(3, "%s", argv[1]);
		break;
	default:
		usage();
	}

	if ((inp = open(inf, O_RDONLY, 0)) < 0)
		err(1, "%s", inf);

	buff = getspace(maxblk);

	if (op == VERIFY) {
		verify(inp, outp, buff);
		exit(0);
	}

	if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
		(void) signal(SIGINT, intr);

	needeof = 0;
	for (lastnread = NOCOUNT;;) {
		if ((nread = read(inp, buff, maxblk)) == -1) {
			while (errno == EINVAL && (maxblk -= 1024)) {
				nread = read(inp, buff, maxblk);
				if (nread >= 0)
					goto r1;
			}
			err(1, "read error, file %d, record %ju", filen, (intmax_t)record);
		} else if (nread != lastnread) {
			if (lastnread != 0 && lastnread != NOCOUNT) {
				if (lastrec == 0 && nread == 0)
					fprintf(msg, "%ju records\n", (intmax_t)record);
				else if (record - lastrec > 1)
					fprintf(msg, "records %ju to %ju\n",
					    (intmax_t)lastrec, (intmax_t)record);
				else
					fprintf(msg, "record %ju\n", (intmax_t)lastrec);
			}
			if (nread != 0)
				fprintf(msg, "file %d: block size %d: ",
				    filen, nread);
			(void) fflush(stdout);
			lastrec = record;
		}
r1:		guesslen = 0;
		if (nread > 0) {
			if (op == COPY || op == COPYVERIFY) {
				if (needeof) {
					writeop(outp, MTWEOF);
					needeof = 0;
				}
				nw = write(outp, buff, nread);
				if (nw != nread) {
					if (nw == -1) {
						warn("write error, file %d, record %ju", filen,
						    (intmax_t)record);
					} else {
						warnx("write error, file %d, record %ju", filen,
						    (intmax_t)record);
						warnx("write (%d) != read (%d)", nw, nread);
					}
					errx(5, "copy aborted");
				}
			}
			size += nread;
			record++;
		} else {
			if (lastnread <= 0 && lastnread != NOCOUNT) {
				fprintf(msg, "eot\n");
				break;
			}
			fprintf(msg,
			    "file %d: eof after %ju records: %ju bytes\n",
			    filen, (intmax_t)record, (intmax_t)size);
			needeof = 1;
			filen++;
			tsize += size;
			size = record = lastrec = 0;
			lastnread = 0;
		}
		lastnread = nread;
	}
	fprintf(msg, "total length: %ju bytes\n", (intmax_t)tsize);
	(void)signal(SIGINT, oldsig);
	if (op == COPY || op == COPYVERIFY) {
		writeop(outp, MTWEOF);
		writeop(outp, MTWEOF);
		if (op == COPYVERIFY) {
			rewind_tape(outp);
			rewind_tape(inp);
			verify(inp, outp, buff);
		}
	}
	exit(0);
}
Example #4
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);
}
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 #7
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);
}
Example #8
0
/*
 * Create and send a signed communication to the ACME server.
 * Stuff the response into the communication buffer.
 * Return <0 on failure on the HTTP error code otherwise.
 */
static long
sreq(struct conn *c, const char *addr, const char *req)
{
	struct httpget	*g;
	struct source	 src[MAX_SERVERS_DNS];
	char		*host, *path, *nonce, *reqsn;
	short		 port;
	struct httphead	*h;
	ssize_t		 ssz;
	long		 code;

	if (NULL == (host = url2host(c->na, &port, &path)))
		return(-1);

	if ((ssz = urlresolve(c->dfd, host, src)) < 0) {
		free(host);
		free(path);
		return(-1);
	}

	g = http_get(src, (size_t)ssz, host, port, path, NULL, 0);
	free(host);
	free(path);
	if (NULL == g)
		return(-1);

	h = http_head_get("Replay-Nonce", g->head, g->headsz);
	if (NULL == h) {
		warnx("%s: no replay nonce", c->na);
		http_get_free(g);
		return(-1);
	} else if (NULL == (nonce = strdup(h->val))) {
		warn("strdup");
		http_get_free(g);
		return(-1);
	}
	http_get_free(g);

	/*
	 * Send the nonce and request payload to the acctproc.
	 * This will create the proper JSON object we need.
	 */

	if (writeop(c->fd, COMM_ACCT, ACCT_SIGN) <= 0) {
		free(nonce);
		return(-1);
	} else if (writestr(c->fd, COMM_PAY, req) <= 0) {
		free(nonce);
		return(-1);
	} else if (writestr(c->fd, COMM_NONCE, nonce) <= 0) {
		free(nonce);
		return(-1);
	}
	free(nonce);

	/* Now read back the signed payload. */

	if (NULL == (reqsn = readstr(c->fd, COMM_REQ)))
		return(-1);

	/* Now send the signed payload to the CA. */

	if (NULL == (host = url2host(addr, &port, &path))) {
		free(reqsn);
		return(-1);
	} else if ((ssz = urlresolve(c->dfd, host, src)) < 0) {
		free(host);
		free(path);
		free(reqsn);
		return(-1);
	}

	g = http_get(src, (size_t)ssz, host,
		port, path, reqsn, strlen(reqsn));

	free(host);
	free(path);
	free(reqsn);
	if (NULL == g)
		return(-1);

	/* Stuff response into parse buffer. */

	code = g->code;

	free(c->buf.buf);
	c->buf.sz = g->bodypartsz;
	c->buf.buf = malloc(c->buf.sz);
	memcpy(c->buf.buf, g->bodypart, c->buf.sz);
	http_get_free(g);
	if (NULL == c->buf.buf) {
		warn("malloc");
		return(-1);
	}
	return(code);
}