xref: /openbsd-src/usr.sbin/acme-client/acctproc.c (revision 06cb0e1175b49d6567d2080fe42b0d5e5258e3e7)
1*06cb0e11Sop /*	$Id: acctproc.c,v 1.32 2023/08/29 14:44:53 op Exp $ */
2de579d12Sflorian /*
3de579d12Sflorian  * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4de579d12Sflorian  *
5de579d12Sflorian  * Permission to use, copy, modify, and distribute this software for any
6de579d12Sflorian  * purpose with or without fee is hereby granted, provided that the above
7de579d12Sflorian  * copyright notice and this permission notice appear in all copies.
8de579d12Sflorian  *
9de579d12Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10de579d12Sflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11de579d12Sflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12de579d12Sflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13de579d12Sflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14de579d12Sflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15de579d12Sflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16de579d12Sflorian  */
17de579d12Sflorian 
18de579d12Sflorian #include <sys/stat.h>
19de579d12Sflorian 
20de579d12Sflorian #include <err.h>
21f7a1d352Stb #include <errno.h>
228672b090Stb #include <limits.h>
23de579d12Sflorian #include <stdio.h>
24de579d12Sflorian #include <stdlib.h>
25de579d12Sflorian #include <string.h>
26de579d12Sflorian #include <unistd.h>
27de579d12Sflorian 
28f7a1d352Stb #include <openssl/bn.h>
29f7a1d352Stb #include <openssl/ec.h>
304f8b772fSflorian #include <openssl/evp.h>
31de579d12Sflorian #include <openssl/rsa.h>
32de579d12Sflorian #include <openssl/err.h>
33de579d12Sflorian 
34de579d12Sflorian #include "extern.h"
353e86e78bSgilles #include "key.h"
36de579d12Sflorian 
37de579d12Sflorian /*
38de579d12Sflorian  * Converts a BIGNUM to the form used in JWK.
39de579d12Sflorian  * This is essentially a base64-encoded big-endian binary string
40de579d12Sflorian  * representation of the number.
41de579d12Sflorian  */
42de579d12Sflorian static char *
bn2string(const BIGNUM * bn)43de579d12Sflorian bn2string(const BIGNUM *bn)
44de579d12Sflorian {
45de579d12Sflorian 	int	 len;
46de579d12Sflorian 	char	*buf, *bbuf;
47de579d12Sflorian 
48de579d12Sflorian 	/* Extract big-endian representation of BIGNUM. */
49de579d12Sflorian 
50de579d12Sflorian 	len = BN_num_bytes(bn);
517cd8f039Sjsing 	if ((buf = malloc(len)) == NULL) {
52de579d12Sflorian 		warn("malloc");
5334335c11Sjsing 		return NULL;
54de579d12Sflorian 	} else if (len != BN_bn2bin(bn, (unsigned char *)buf)) {
55de579d12Sflorian 		warnx("BN_bn2bin");
56de579d12Sflorian 		free(buf);
5734335c11Sjsing 		return NULL;
58de579d12Sflorian 	}
59de579d12Sflorian 
60de579d12Sflorian 	/* Convert to base64url. */
61de579d12Sflorian 
627cd8f039Sjsing 	if ((bbuf = base64buf_url(buf, len)) == NULL) {
63de579d12Sflorian 		warnx("base64buf_url");
64de579d12Sflorian 		free(buf);
6534335c11Sjsing 		return NULL;
66de579d12Sflorian 	}
67de579d12Sflorian 
68de579d12Sflorian 	free(buf);
6934335c11Sjsing 	return bbuf;
70de579d12Sflorian }
71de579d12Sflorian 
72de579d12Sflorian /*
73de579d12Sflorian  * Extract the relevant RSA components from the key and create the JSON
74de579d12Sflorian  * thumbprint from them.
75de579d12Sflorian  */
76de579d12Sflorian static char *
op_thumb_rsa(EVP_PKEY * pkey)77de579d12Sflorian op_thumb_rsa(EVP_PKEY *pkey)
78de579d12Sflorian {
797bce6888Sderaadt 	char	*exp = NULL, *mod = NULL, *json = NULL;
80de579d12Sflorian 	RSA	*r;
81de579d12Sflorian 
82738ebed2Sflorian 	if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL)
83c313e8c2Sflorian 		warnx("EVP_PKEY_get0_RSA");
84d8ad561fStb 	else if ((mod = bn2string(RSA_get0_n(r))) == NULL)
85de579d12Sflorian 		warnx("bn2string");
86d8ad561fStb 	else if ((exp = bn2string(RSA_get0_e(r))) == NULL)
87de579d12Sflorian 		warnx("bn2string");
887cd8f039Sjsing 	else if ((json = json_fmt_thumb_rsa(exp, mod)) == NULL)
89de579d12Sflorian 		warnx("json_fmt_thumb_rsa");
90de579d12Sflorian 
91de579d12Sflorian 	free(exp);
92de579d12Sflorian 	free(mod);
9334335c11Sjsing 	return json;
94de579d12Sflorian }
95de579d12Sflorian 
96de579d12Sflorian /*
974f8b772fSflorian  * Extract the relevant EC components from the key and create the JSON
984f8b772fSflorian  * thumbprint from them.
994f8b772fSflorian  */
1004f8b772fSflorian static char *
op_thumb_ec(EVP_PKEY * pkey)1014f8b772fSflorian op_thumb_ec(EVP_PKEY *pkey)
1024f8b772fSflorian {
1034f8b772fSflorian 	BIGNUM	*X = NULL, *Y = NULL;
1044f8b772fSflorian 	EC_KEY	*ec = NULL;
1054f8b772fSflorian 	char	*x = NULL, *y = NULL;
1064f8b772fSflorian 	char	*json = NULL;
1074f8b772fSflorian 
1084f8b772fSflorian 	if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
1094f8b772fSflorian 		warnx("EVP_PKEY_get0_EC_KEY");
1104f8b772fSflorian 	else if ((X = BN_new()) == NULL)
1114f8b772fSflorian 		warnx("BN_new");
1124f8b772fSflorian 	else if ((Y = BN_new()) == NULL)
1134f8b772fSflorian 		warnx("BN_new");
114a72a9054Stb 	else if (!EC_POINT_get_affine_coordinates(EC_KEY_get0_group(ec),
1154f8b772fSflorian 	    EC_KEY_get0_public_key(ec), X, Y, NULL))
116a72a9054Stb 		warnx("EC_POINT_get_affine_coordinates");
1174f8b772fSflorian 	else if ((x = bn2string(X)) == NULL)
1184f8b772fSflorian 		warnx("bn2string");
1194f8b772fSflorian 	else if ((y = bn2string(Y)) == NULL)
1204f8b772fSflorian 		warnx("bn2string");
1214f8b772fSflorian 	else if ((json = json_fmt_thumb_ec(x, y)) == NULL)
12223921d49Stb 		warnx("json_fmt_thumb_ec");
1234f8b772fSflorian 
1244f8b772fSflorian 	BN_free(X);
1254f8b772fSflorian 	BN_free(Y);
1264f8b772fSflorian 	free(x);
1274f8b772fSflorian 	free(y);
1284f8b772fSflorian 	return json;
1294f8b772fSflorian }
1304f8b772fSflorian 
1314f8b772fSflorian /*
132de579d12Sflorian  * The thumbprint operation is used for the challenge sequence.
133de579d12Sflorian  */
134de579d12Sflorian static int
op_thumbprint(int fd,EVP_PKEY * pkey)135de579d12Sflorian op_thumbprint(int fd, EVP_PKEY *pkey)
136de579d12Sflorian {
1377bce6888Sderaadt 	char		*thumb = NULL, *dig64 = NULL;
138d1e21fa9Stb 	unsigned char	 dig[EVP_MAX_MD_SIZE];
139de579d12Sflorian 	unsigned int	 digsz;
1407bce6888Sderaadt 	int		 rc = 0;
141de579d12Sflorian 
142de579d12Sflorian 	/* Construct the thumbprint input itself. */
143de579d12Sflorian 
144e9900e80Stb 	switch (EVP_PKEY_base_id(pkey)) {
145de579d12Sflorian 	case EVP_PKEY_RSA:
1467cd8f039Sjsing 		if ((thumb = op_thumb_rsa(pkey)) != NULL)
147de579d12Sflorian 			break;
148de579d12Sflorian 		goto out;
1494f8b772fSflorian 	case EVP_PKEY_EC:
1504f8b772fSflorian 		if ((thumb = op_thumb_ec(pkey)) != NULL)
1514f8b772fSflorian 			break;
1524f8b772fSflorian 		goto out;
153de579d12Sflorian 	default:
154e9900e80Stb 		warnx("EVP_PKEY_base_id: unknown key type");
155de579d12Sflorian 		goto out;
156de579d12Sflorian 	}
157de579d12Sflorian 
158de579d12Sflorian 	/*
159de579d12Sflorian 	 * Compute the SHA256 digest of the thumbprint then
160de579d12Sflorian 	 * base64-encode the digest itself.
161de579d12Sflorian 	 * If the reader is closed when we write, ignore it (we'll pick
162de579d12Sflorian 	 * it up in the read loop).
163de579d12Sflorian 	 */
164de579d12Sflorian 
165d1e21fa9Stb 	if (!EVP_Digest(thumb, strlen(thumb), dig, &digsz, EVP_sha256(),
166d1e21fa9Stb 	    NULL)) {
167d1e21fa9Stb 		warnx("EVP_Digest");
168de579d12Sflorian 		goto out;
169d1e21fa9Stb 	}
170d1e21fa9Stb 	if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) {
171de579d12Sflorian 		warnx("base64buf_url");
172de579d12Sflorian 		goto out;
173d1e21fa9Stb 	}
174d1e21fa9Stb 	if (writestr(fd, COMM_THUMB, dig64) < 0)
175de579d12Sflorian 		goto out;
176de579d12Sflorian 
177de579d12Sflorian 	rc = 1;
178de579d12Sflorian out:
179de579d12Sflorian 	free(thumb);
180de579d12Sflorian 	free(dig64);
18134335c11Sjsing 	return rc;
182de579d12Sflorian }
183de579d12Sflorian 
184de579d12Sflorian static int
op_sign_rsa(char ** prot,EVP_PKEY * pkey,const char * nonce,const char * url)1857b00f4e9Sflorian op_sign_rsa(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
186de579d12Sflorian {
1877bce6888Sderaadt 	char	*exp = NULL, *mod = NULL;
1887bce6888Sderaadt 	int	rc = 0;
189de579d12Sflorian 	RSA	*r;
190de579d12Sflorian 
1917bce6888Sderaadt 	*prot = NULL;
192de579d12Sflorian 
193de579d12Sflorian 	/*
194de579d12Sflorian 	 * First, extract relevant portions of our private key.
195de579d12Sflorian 	 * Finally, format the header combined with the nonce.
196de579d12Sflorian 	 */
197de579d12Sflorian 
198738ebed2Sflorian 	if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL)
199c313e8c2Sflorian 		warnx("EVP_PKEY_get0_RSA");
200d8ad561fStb 	else if ((mod = bn2string(RSA_get0_n(r))) == NULL)
201de579d12Sflorian 		warnx("bn2string");
202d8ad561fStb 	else if ((exp = bn2string(RSA_get0_e(r))) == NULL)
203de579d12Sflorian 		warnx("bn2string");
2047b00f4e9Sflorian 	else if ((*prot = json_fmt_protected_rsa(exp, mod, nonce, url)) == NULL)
205de579d12Sflorian 		warnx("json_fmt_protected_rsa");
206de579d12Sflorian 	else
207de579d12Sflorian 		rc = 1;
208de579d12Sflorian 
209de579d12Sflorian 	free(exp);
210de579d12Sflorian 	free(mod);
21134335c11Sjsing 	return rc;
212de579d12Sflorian }
213de579d12Sflorian 
2144f8b772fSflorian static int
op_sign_ec(char ** prot,EVP_PKEY * pkey,const char * nonce,const char * url)2154f8b772fSflorian op_sign_ec(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
2164f8b772fSflorian {
2174f8b772fSflorian 	BIGNUM	*X = NULL, *Y = NULL;
2184f8b772fSflorian 	EC_KEY	*ec = NULL;
2194f8b772fSflorian 	char	*x = NULL, *y = NULL;
2204f8b772fSflorian 	int	rc = 0;
2214f8b772fSflorian 
2224f8b772fSflorian 	*prot = NULL;
2234f8b772fSflorian 
2244f8b772fSflorian 	if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
2254f8b772fSflorian 		warnx("EVP_PKEY_get0_EC_KEY");
2264f8b772fSflorian 	else if ((X = BN_new()) == NULL)
2274f8b772fSflorian 		warnx("BN_new");
2284f8b772fSflorian 	else if ((Y = BN_new()) == NULL)
2294f8b772fSflorian 		warnx("BN_new");
230a72a9054Stb 	else if (!EC_POINT_get_affine_coordinates(EC_KEY_get0_group(ec),
2314f8b772fSflorian 	    EC_KEY_get0_public_key(ec), X, Y, NULL))
232a72a9054Stb 		warnx("EC_POINT_get_affine_coordinates");
2334f8b772fSflorian 	else if ((x = bn2string(X)) == NULL)
2344f8b772fSflorian 		warnx("bn2string");
2354f8b772fSflorian 	else if ((y = bn2string(Y)) == NULL)
2364f8b772fSflorian 		warnx("bn2string");
2374f8b772fSflorian 	else if ((*prot = json_fmt_protected_ec(x, y, nonce, url)) == NULL)
2384f8b772fSflorian 		warnx("json_fmt_protected_ec");
2394f8b772fSflorian 	else
2404f8b772fSflorian 		rc = 1;
2414f8b772fSflorian 
2424f8b772fSflorian 	BN_free(X);
2434f8b772fSflorian 	BN_free(Y);
2444f8b772fSflorian 	free(x);
2454f8b772fSflorian 	free(y);
2464f8b772fSflorian 	return rc;
2474f8b772fSflorian }
2484f8b772fSflorian 
249de579d12Sflorian /*
250de579d12Sflorian  * Operation to sign a message with the account key.
251de579d12Sflorian  * This requires the sender ("fd") to provide the payload and a nonce.
252de579d12Sflorian  */
253de579d12Sflorian static int
op_sign(int fd,EVP_PKEY * pkey,enum acctop op)2547b00f4e9Sflorian op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
255de579d12Sflorian {
2564f8b772fSflorian 	EVP_MD_CTX		*ctx = NULL;
2574f8b772fSflorian 	const EVP_MD		*evp_md = NULL;
2584f8b772fSflorian 	ECDSA_SIG		*ec_sig = NULL;
2594f8b772fSflorian 	const BIGNUM		*ec_sig_r = NULL, *ec_sig_s = NULL;
2603ac75170Stb 	int			 bn_len, sign_len, rc = 0;
2617bce6888Sderaadt 	char			*nonce = NULL, *pay = NULL, *pay64 = NULL;
2627b00f4e9Sflorian 	char			*prot = NULL, *prot64 = NULL;
2637bce6888Sderaadt 	char			*sign = NULL, *dig64 = NULL, *fin = NULL;
2644f8b772fSflorian 	char			*url = NULL, *kid = NULL, *alg = NULL;
2654f8b772fSflorian 	const unsigned char	*digp;
2665ab53ec1Stb 	unsigned char		*dig = NULL, *buf = NULL;
2675ab53ec1Stb 	size_t			 digsz;
268de579d12Sflorian 
269de579d12Sflorian 	/* Read our payload and nonce from the requestor. */
270de579d12Sflorian 
2717cd8f039Sjsing 	if ((pay = readstr(fd, COMM_PAY)) == NULL)
272de579d12Sflorian 		goto out;
2737cd8f039Sjsing 	else if ((nonce = readstr(fd, COMM_NONCE)) == NULL)
274de579d12Sflorian 		goto out;
2757b00f4e9Sflorian 	else if ((url = readstr(fd, COMM_URL)) == NULL)
2767b00f4e9Sflorian 		goto out;
2777b00f4e9Sflorian 
2787b00f4e9Sflorian 	if (op == ACCT_KID_SIGN)
2797b00f4e9Sflorian 		if ((kid = readstr(fd, COMM_KID)) == NULL)
2807b00f4e9Sflorian 			goto out;
281de579d12Sflorian 
282de579d12Sflorian 	/* Base64-encode the payload. */
283de579d12Sflorian 
2847cd8f039Sjsing 	if ((pay64 = base64buf_url(pay, strlen(pay))) == NULL) {
285de579d12Sflorian 		warnx("base64buf_url");
286de579d12Sflorian 		goto out;
287de579d12Sflorian 	}
288de579d12Sflorian 
289e9900e80Stb 	switch (EVP_PKEY_base_id(pkey)) {
2904f8b772fSflorian 	case EVP_PKEY_RSA:
2914f8b772fSflorian 		alg = "RS256";
2924f8b772fSflorian 		evp_md = EVP_sha256();
2934f8b772fSflorian 		break;
2944f8b772fSflorian 	case EVP_PKEY_EC:
2954f8b772fSflorian 		alg = "ES384";
2964f8b772fSflorian 		evp_md = EVP_sha384();
2974f8b772fSflorian 		break;
2984f8b772fSflorian 	default:
2994f8b772fSflorian 		warnx("unknown account key type");
3004f8b772fSflorian 		goto out;
3014f8b772fSflorian 	}
3024f8b772fSflorian 
3037b00f4e9Sflorian 	if (op == ACCT_KID_SIGN) {
3044f8b772fSflorian 		if ((prot = json_fmt_protected_kid(alg, kid, nonce, url)) ==
3054f8b772fSflorian 		    NULL) {
3067b00f4e9Sflorian 			warnx("json_fmt_protected_kid");
3077b00f4e9Sflorian 			goto out;
3087b00f4e9Sflorian 		}
3097b00f4e9Sflorian 	} else {
310e9900e80Stb 		switch (EVP_PKEY_base_id(pkey)) {
311de579d12Sflorian 		case EVP_PKEY_RSA:
3127b00f4e9Sflorian 			if (!op_sign_rsa(&prot, pkey, nonce, url))
313de579d12Sflorian 				goto out;
314de579d12Sflorian 			break;
3154f8b772fSflorian 		case EVP_PKEY_EC:
3164f8b772fSflorian 			if (!op_sign_ec(&prot, pkey, nonce, url))
3174f8b772fSflorian 				goto out;
3184f8b772fSflorian 			break;
319de579d12Sflorian 		default:
320e9900e80Stb 			warnx("EVP_PKEY_base_id");
321de579d12Sflorian 			goto out;
322de579d12Sflorian 		}
3237b00f4e9Sflorian 	}
324de579d12Sflorian 
325de579d12Sflorian 	/* The header combined with the nonce, base64. */
326de579d12Sflorian 
3277cd8f039Sjsing 	if ((prot64 = base64buf_url(prot, strlen(prot))) == NULL) {
328de579d12Sflorian 		warnx("base64buf_url");
329de579d12Sflorian 		goto out;
330de579d12Sflorian 	}
331de579d12Sflorian 
332de579d12Sflorian 	/* Now the signature material. */
333de579d12Sflorian 
3343ac75170Stb 	sign_len = asprintf(&sign, "%s.%s", prot64, pay64);
3353ac75170Stb 	if (sign_len == -1) {
336de579d12Sflorian 		warn("asprintf");
337de579d12Sflorian 		sign = NULL;
338de579d12Sflorian 		goto out;
339de579d12Sflorian 	}
340de579d12Sflorian 
3415ab53ec1Stb 	/* Sign the message. */
342de579d12Sflorian 
3431288ee49Sflorian 	if ((ctx = EVP_MD_CTX_new()) == NULL) {
3441288ee49Sflorian 		warnx("EVP_MD_CTX_new");
345de579d12Sflorian 		goto out;
3465ab53ec1Stb 	}
3475ab53ec1Stb 	if (!EVP_DigestSignInit(ctx, NULL, evp_md, NULL, pkey)) {
3485ab53ec1Stb 		warnx("EVP_DigestSignInit");
349de579d12Sflorian 		goto out;
3505ab53ec1Stb 	}
3513ac75170Stb 	if (!EVP_DigestSign(ctx, NULL, &digsz, sign, sign_len)) {
3525ab53ec1Stb 		warnx("EVP_DigestSign");
353de579d12Sflorian 		goto out;
3545ab53ec1Stb 	}
3555ab53ec1Stb 	if ((dig = malloc(digsz)) == NULL) {
3565ab53ec1Stb 		warn("malloc");
3575ab53ec1Stb 		goto out;
3585ab53ec1Stb 	}
3593ac75170Stb 	if (!EVP_DigestSign(ctx, dig, &digsz, sign, sign_len)) {
3605ab53ec1Stb 		warnx("EVP_DigestSign");
361de579d12Sflorian 		goto out;
3624f8b772fSflorian 	}
3634f8b772fSflorian 
364e9900e80Stb 	switch (EVP_PKEY_base_id(pkey)) {
3654f8b772fSflorian 	case EVP_PKEY_RSA:
3664f8b772fSflorian 		if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) {
367de579d12Sflorian 			warnx("base64buf_url");
368de579d12Sflorian 			goto out;
369de579d12Sflorian 		}
3704f8b772fSflorian 		break;
3714f8b772fSflorian 	case EVP_PKEY_EC:
3728672b090Stb 		if (digsz > LONG_MAX) {
3738672b090Stb 			warnx("EC signature too long");
3748672b090Stb 			goto out;
3758672b090Stb 		}
3768672b090Stb 
377eb2dd7b0Stb 		digp = dig;
3784f8b772fSflorian 		if ((ec_sig = d2i_ECDSA_SIG(NULL, &digp, digsz)) == NULL) {
3794f8b772fSflorian 			warnx("d2i_ECDSA_SIG");
3804f8b772fSflorian 			goto out;
3814f8b772fSflorian 		}
3824f8b772fSflorian 
383eb2dd7b0Stb 		if ((ec_sig_r = ECDSA_SIG_get0_r(ec_sig)) == NULL ||
384eb2dd7b0Stb 		    (ec_sig_s = ECDSA_SIG_get0_s(ec_sig)) == NULL) {
3854f8b772fSflorian 			warnx("ECDSA_SIG_get0");
3864f8b772fSflorian 			goto out;
3874f8b772fSflorian 		}
3884f8b772fSflorian 
389eb2dd7b0Stb 		if ((bn_len = (EVP_PKEY_bits(pkey) + 7) / 8) <= 0) {
390eb2dd7b0Stb 			warnx("EVP_PKEY_bits");
391eb2dd7b0Stb 			goto out;
392eb2dd7b0Stb 		}
393eb2dd7b0Stb 
394eb2dd7b0Stb 		if ((buf = calloc(2, bn_len)) == NULL) {
3954f8b772fSflorian 			warnx("calloc");
3964f8b772fSflorian 			goto out;
3974f8b772fSflorian 		}
3984f8b772fSflorian 
399eb2dd7b0Stb 		if (BN_bn2binpad(ec_sig_r, buf, bn_len) != bn_len ||
400eb2dd7b0Stb 		    BN_bn2binpad(ec_sig_s, buf + bn_len, bn_len) != bn_len) {
401eb2dd7b0Stb 			warnx("BN_bn2binpad");
402eb2dd7b0Stb 			goto out;
403eb2dd7b0Stb 		}
4044f8b772fSflorian 
405eb2dd7b0Stb 		if ((dig64 = base64buf_url((char *)buf, 2 * bn_len)) == NULL) {
4064f8b772fSflorian 			warnx("base64buf_url");
4074f8b772fSflorian 			goto out;
4084f8b772fSflorian 		}
4094f8b772fSflorian 
4104f8b772fSflorian 		break;
4114f8b772fSflorian 	default:
412e9900e80Stb 		warnx("EVP_PKEY_base_id");
4134f8b772fSflorian 		goto out;
4144f8b772fSflorian 	}
415de579d12Sflorian 
416de579d12Sflorian 	/*
417de579d12Sflorian 	 * Write back in the correct JSON format.
418de579d12Sflorian 	 * If the reader is closed, just ignore it (we'll pick it up
419de579d12Sflorian 	 * when we next enter the read loop).
420de579d12Sflorian 	 */
421de579d12Sflorian 
4227b00f4e9Sflorian 	if ((fin = json_fmt_signed(prot64, pay64, dig64)) == NULL) {
423de579d12Sflorian 		warnx("json_fmt_signed");
424de579d12Sflorian 		goto out;
425de579d12Sflorian 	} else if (writestr(fd, COMM_REQ, fin) < 0)
426de579d12Sflorian 		goto out;
427de579d12Sflorian 
428de579d12Sflorian 	rc = 1;
429de579d12Sflorian out:
43078a064a0Stb 	ECDSA_SIG_free(ec_sig);
4311288ee49Sflorian 	EVP_MD_CTX_free(ctx);
432de579d12Sflorian 	free(pay);
433de579d12Sflorian 	free(sign);
434de579d12Sflorian 	free(pay64);
4357b00f4e9Sflorian 	free(url);
436de579d12Sflorian 	free(nonce);
4377b00f4e9Sflorian 	free(kid);
438de579d12Sflorian 	free(prot);
439de579d12Sflorian 	free(prot64);
440de579d12Sflorian 	free(dig);
441de579d12Sflorian 	free(dig64);
442de579d12Sflorian 	free(fin);
4434f8b772fSflorian 	free(buf);
44434335c11Sjsing 	return rc;
445de579d12Sflorian }
446de579d12Sflorian 
447de579d12Sflorian int
acctproc(int netsock,const char * acctkey,enum keytype keytype)4484f8b772fSflorian acctproc(int netsock, const char *acctkey, enum keytype keytype)
449de579d12Sflorian {
4507bce6888Sderaadt 	FILE		*f = NULL;
4517bce6888Sderaadt 	EVP_PKEY	*pkey = NULL;
452de579d12Sflorian 	long		 lval;
453de579d12Sflorian 	enum acctop	 op;
4542570ecd0Sflorian 	int		 rc = 0, cc, newacct = 0;
455de579d12Sflorian 	mode_t		 prev;
456de579d12Sflorian 
457de579d12Sflorian 	/*
458de579d12Sflorian 	 * First, open our private key file read-only or write-only if
459de579d12Sflorian 	 * we're creating from scratch.
460de579d12Sflorian 	 * Set our umask to be maximally restrictive.
461de579d12Sflorian 	 */
462de579d12Sflorian 
463de579d12Sflorian 	prev = umask((S_IWUSR | S_IXUSR) | S_IRWXG | S_IRWXO);
4642570ecd0Sflorian 	if ((f = fopen(acctkey, "r")) == NULL && errno == ENOENT) {
4652570ecd0Sflorian 		f = fopen(acctkey, "wx");
4662570ecd0Sflorian 		newacct = 1;
4672570ecd0Sflorian 	}
468de579d12Sflorian 	umask(prev);
469de579d12Sflorian 
4707cd8f039Sjsing 	if (f == NULL) {
471de579d12Sflorian 		warn("%s", acctkey);
472de579d12Sflorian 		goto out;
473de579d12Sflorian 	}
474de579d12Sflorian 
475de579d12Sflorian 	/* File-system, user, and sandbox jailing. */
476de579d12Sflorian 
477de579d12Sflorian 	ERR_load_crypto_strings();
478de579d12Sflorian 
479ec0d8c8bSderaadt 	if (pledge("stdio", NULL) == -1) {
480ec0d8c8bSderaadt 		warn("pledge");
481de579d12Sflorian 		goto out;
482ec0d8c8bSderaadt 	}
483de579d12Sflorian 
484de579d12Sflorian 	if (newacct) {
4854f8b772fSflorian 		switch (keytype) {
4864f8b772fSflorian 		case KT_ECDSA:
4874f8b772fSflorian 			if ((pkey = ec_key_create(f, acctkey)) == NULL)
4884f8b772fSflorian 				goto out;
4894f8b772fSflorian 			dodbg("%s: generated ECDSA account key", acctkey);
4904f8b772fSflorian 			break;
4914f8b772fSflorian 		case KT_RSA:
4927cd8f039Sjsing 			if ((pkey = rsa_key_create(f, acctkey)) == NULL)
493de579d12Sflorian 				goto out;
494de579d12Sflorian 			dodbg("%s: generated RSA account key", acctkey);
4954f8b772fSflorian 			break;
4964f8b772fSflorian 		}
497de579d12Sflorian 	} else {
4983e86e78bSgilles 		if ((pkey = key_load(f, acctkey)) == NULL)
4993e86e78bSgilles 			goto out;
5004f8b772fSflorian 		/* XXX check if account key type equals configured key type */
5014f8b772fSflorian 		doddbg("%s: loaded account key", acctkey);
502de579d12Sflorian 	}
503de579d12Sflorian 
504de579d12Sflorian 	fclose(f);
505de579d12Sflorian 	f = NULL;
506de579d12Sflorian 
507de579d12Sflorian 	/* Notify the netproc that we've started up. */
508de579d12Sflorian 
5097cd8f039Sjsing 	if ((cc = writeop(netsock, COMM_ACCT_STAT, ACCT_READY)) == 0)
510de579d12Sflorian 		rc = 1;
511de579d12Sflorian 	if (cc <= 0)
512de579d12Sflorian 		goto out;
513de579d12Sflorian 
514de579d12Sflorian 	/*
515de579d12Sflorian 	 * Now we wait for requests from the network-facing process.
516de579d12Sflorian 	 * It might ask us for our thumbprint, for example, or for us to
517de579d12Sflorian 	 * sign a message.
518de579d12Sflorian 	 */
519de579d12Sflorian 
520de579d12Sflorian 	for (;;) {
521de579d12Sflorian 		op = ACCT__MAX;
5227cd8f039Sjsing 		if ((lval = readop(netsock, COMM_ACCT)) == 0)
523de579d12Sflorian 			op = ACCT_STOP;
5247b00f4e9Sflorian 		else if (lval == ACCT_SIGN || lval == ACCT_KID_SIGN ||
5257b00f4e9Sflorian 		    lval == ACCT_THUMBPRINT)
526de579d12Sflorian 			op = lval;
527de579d12Sflorian 
528de579d12Sflorian 		if (ACCT__MAX == op) {
529de579d12Sflorian 			warnx("unknown operation from netproc");
530de579d12Sflorian 			goto out;
531de579d12Sflorian 		} else if (ACCT_STOP == op)
532de579d12Sflorian 			break;
533de579d12Sflorian 
534de579d12Sflorian 		switch (op) {
535a8699559Sderaadt 		case ACCT_SIGN:
5367b00f4e9Sflorian 		case ACCT_KID_SIGN:
5377b00f4e9Sflorian 			if (op_sign(netsock, pkey, op))
538de579d12Sflorian 				break;
539de579d12Sflorian 			warnx("op_sign");
540de579d12Sflorian 			goto out;
541a8699559Sderaadt 		case ACCT_THUMBPRINT:
542de579d12Sflorian 			if (op_thumbprint(netsock, pkey))
543de579d12Sflorian 				break;
544de579d12Sflorian 			warnx("op_thumbprint");
545de579d12Sflorian 			goto out;
546de579d12Sflorian 		default:
547de579d12Sflorian 			abort();
548de579d12Sflorian 		}
549de579d12Sflorian 	}
550de579d12Sflorian 
551de579d12Sflorian 	rc = 1;
552de579d12Sflorian out:
553de579d12Sflorian 	close(netsock);
5547cd8f039Sjsing 	if (f != NULL)
555de579d12Sflorian 		fclose(f);
556de579d12Sflorian 	EVP_PKEY_free(pkey);
557de579d12Sflorian 	ERR_print_errors_fp(stderr);
558de579d12Sflorian 	ERR_free_strings();
55934335c11Sjsing 	return rc;
560de579d12Sflorian }
561