xref: /openbsd/usr.sbin/acme-client/acctproc.c (revision 274d7c50)
1 /*	$Id: acctproc.c,v 1.20 2019/06/17 15:20:10 tb Exp $ */
2 /*
3  * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/stat.h>
19 
20 #include <err.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include <openssl/pem.h>
27 #include <openssl/evp.h>
28 #include <openssl/rsa.h>
29 #include <openssl/rand.h>
30 #include <openssl/err.h>
31 
32 #include "extern.h"
33 #include "key.h"
34 
35 /*
36  * Converts a BIGNUM to the form used in JWK.
37  * This is essentially a base64-encoded big-endian binary string
38  * representation of the number.
39  */
40 static char *
41 bn2string(const BIGNUM *bn)
42 {
43 	int	 len;
44 	char	*buf, *bbuf;
45 
46 	/* Extract big-endian representation of BIGNUM. */
47 
48 	len = BN_num_bytes(bn);
49 	if ((buf = malloc(len)) == NULL) {
50 		warn("malloc");
51 		return NULL;
52 	} else if (len != BN_bn2bin(bn, (unsigned char *)buf)) {
53 		warnx("BN_bn2bin");
54 		free(buf);
55 		return NULL;
56 	}
57 
58 	/* Convert to base64url. */
59 
60 	if ((bbuf = base64buf_url(buf, len)) == NULL) {
61 		warnx("base64buf_url");
62 		free(buf);
63 		return NULL;
64 	}
65 
66 	free(buf);
67 	return bbuf;
68 }
69 
70 /*
71  * Extract the relevant RSA components from the key and create the JSON
72  * thumbprint from them.
73  */
74 static char *
75 op_thumb_rsa(EVP_PKEY *pkey)
76 {
77 	char	*exp = NULL, *mod = NULL, *json = NULL;
78 	RSA	*r;
79 
80 	if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL)
81 		warnx("EVP_PKEY_get0_RSA");
82 	else if ((mod = bn2string(r->n)) == NULL)
83 		warnx("bn2string");
84 	else if ((exp = bn2string(r->e)) == NULL)
85 		warnx("bn2string");
86 	else if ((json = json_fmt_thumb_rsa(exp, mod)) == NULL)
87 		warnx("json_fmt_thumb_rsa");
88 
89 	free(exp);
90 	free(mod);
91 	return json;
92 }
93 
94 /*
95  * Extract the relevant EC components from the key and create the JSON
96  * thumbprint from them.
97  */
98 static char *
99 op_thumb_ec(EVP_PKEY *pkey)
100 {
101 	BIGNUM	*X = NULL, *Y = NULL;
102 	EC_KEY	*ec = NULL;
103 	char	*x = NULL, *y = NULL;
104 	char	*json = NULL;
105 
106 	if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
107 		warnx("EVP_PKEY_get0_EC_KEY");
108 	else if ((X = BN_new()) == NULL)
109 		warnx("BN_new");
110 	else if ((Y = BN_new()) == NULL)
111 		warnx("BN_new");
112 	else if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec),
113 	    EC_KEY_get0_public_key(ec), X, Y, NULL))
114 		warnx("EC_POINT_get_affine_coordinates_GFp");
115 	else if ((x = bn2string(X)) == NULL)
116 		warnx("bn2string");
117 	else if ((y = bn2string(Y)) == NULL)
118 		warnx("bn2string");
119 	else if ((json = json_fmt_thumb_ec(x, y)) == NULL)
120 		warnx("json_fmt_thumb_rsa");
121 
122 	BN_free(X);
123 	BN_free(Y);
124 	free(x);
125 	free(y);
126 	return json;
127 }
128 
129 /*
130  * The thumbprint operation is used for the challenge sequence.
131  */
132 static int
133 op_thumbprint(int fd, EVP_PKEY *pkey)
134 {
135 	char		*thumb = NULL, *dig64 = NULL;
136 	EVP_MD_CTX	*ctx = NULL;
137 	unsigned char	*dig = NULL;
138 	unsigned int	 digsz;
139 	int		 rc = 0;
140 
141 	/* Construct the thumbprint input itself. */
142 
143 	switch (EVP_PKEY_type(pkey->type)) {
144 	case EVP_PKEY_RSA:
145 		if ((thumb = op_thumb_rsa(pkey)) != NULL)
146 			break;
147 		goto out;
148 	case EVP_PKEY_EC:
149 		if ((thumb = op_thumb_ec(pkey)) != NULL)
150 			break;
151 		goto out;
152 	default:
153 		warnx("EVP_PKEY_type: unknown key type");
154 		goto out;
155 	}
156 
157 	/*
158 	 * Compute the SHA256 digest of the thumbprint then
159 	 * base64-encode the digest itself.
160 	 * If the reader is closed when we write, ignore it (we'll pick
161 	 * it up in the read loop).
162 	 */
163 
164 	if ((dig = malloc(EVP_MAX_MD_SIZE)) == NULL) {
165 		warn("malloc");
166 		goto out;
167 	} else if ((ctx = EVP_MD_CTX_new()) == NULL) {
168 		warnx("EVP_MD_CTX_new");
169 		goto out;
170 	} else if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) {
171 		warnx("EVP_SignInit_ex");
172 		goto out;
173 	} else if (!EVP_DigestUpdate(ctx, thumb, strlen(thumb))) {
174 		warnx("EVP_SignUpdate");
175 		goto out;
176 	} else if (!EVP_DigestFinal_ex(ctx, dig, &digsz)) {
177 		warnx("EVP_SignFinal");
178 		goto out;
179 	} else if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) {
180 		warnx("base64buf_url");
181 		goto out;
182 	} else if (writestr(fd, COMM_THUMB, dig64) < 0)
183 		goto out;
184 
185 	rc = 1;
186 out:
187 	EVP_MD_CTX_free(ctx);
188 	free(thumb);
189 	free(dig);
190 	free(dig64);
191 	return rc;
192 }
193 
194 static int
195 op_sign_rsa(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
196 {
197 	char	*exp = NULL, *mod = NULL;
198 	int	rc = 0;
199 	RSA	*r;
200 
201 	*prot = NULL;
202 
203 	/*
204 	 * First, extract relevant portions of our private key.
205 	 * Finally, format the header combined with the nonce.
206 	 */
207 
208 	if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL)
209 		warnx("EVP_PKEY_get0_RSA");
210 	else if ((mod = bn2string(r->n)) == NULL)
211 		warnx("bn2string");
212 	else if ((exp = bn2string(r->e)) == NULL)
213 		warnx("bn2string");
214 	else if ((*prot = json_fmt_protected_rsa(exp, mod, nonce, url)) == NULL)
215 		warnx("json_fmt_protected_rsa");
216 	else
217 		rc = 1;
218 
219 	free(exp);
220 	free(mod);
221 	return rc;
222 }
223 
224 static int
225 op_sign_ec(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
226 {
227 	BIGNUM	*X = NULL, *Y = NULL;
228 	EC_KEY	*ec = NULL;
229 	char	*x = NULL, *y = NULL;
230 	int	rc = 0;
231 
232 	*prot = NULL;
233 
234 	if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
235 		warnx("EVP_PKEY_get0_EC_KEY");
236 	else if ((X = BN_new()) == NULL)
237 		warnx("BN_new");
238 	else if ((Y = BN_new()) == NULL)
239 		warnx("BN_new");
240 	else if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec),
241 	    EC_KEY_get0_public_key(ec), X, Y, NULL))
242 		warnx("EC_POINT_get_affine_coordinates_GFp");
243 	else if ((x = bn2string(X)) == NULL)
244 		warnx("bn2string");
245 	else if ((y = bn2string(Y)) == NULL)
246 		warnx("bn2string");
247 	else if ((*prot = json_fmt_protected_ec(x, y, nonce, url)) == NULL)
248 		warnx("json_fmt_protected_ec");
249 	else
250 		rc = 1;
251 
252 	BN_free(X);
253 	BN_free(Y);
254 	free(x);
255 	free(y);
256 	return rc;
257 }
258 
259 /*
260  * Operation to sign a message with the account key.
261  * This requires the sender ("fd") to provide the payload and a nonce.
262  */
263 static int
264 op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
265 {
266 	EVP_MD_CTX		*ctx = NULL;
267 	const EVP_MD		*evp_md = NULL;
268 	EC_KEY			*ec;
269 	ECDSA_SIG		*ec_sig = NULL;
270 	const BIGNUM		*ec_sig_r = NULL, *ec_sig_s = NULL;
271 	int			 cc, rc = 0;
272 	unsigned int		 digsz, bufsz, degree, bn_len, r_len, s_len;
273 	char			*nonce = NULL, *pay = NULL, *pay64 = NULL;
274 	char			*prot = NULL, *prot64 = NULL;
275 	char			*sign = NULL, *dig64 = NULL, *fin = NULL;
276 	char			*url = NULL, *kid = NULL, *alg = NULL;
277 	unsigned char		*dig = NULL, *buf = NULL;
278 	const unsigned char	*digp;
279 
280 	/* Read our payload and nonce from the requestor. */
281 
282 	if ((pay = readstr(fd, COMM_PAY)) == NULL)
283 		goto out;
284 	else if ((nonce = readstr(fd, COMM_NONCE)) == NULL)
285 		goto out;
286 	else if ((url = readstr(fd, COMM_URL)) == NULL)
287 		goto out;
288 
289 	if (op == ACCT_KID_SIGN)
290 		if ((kid = readstr(fd, COMM_KID)) == NULL)
291 			goto out;
292 
293 	/* Base64-encode the payload. */
294 
295 	if ((pay64 = base64buf_url(pay, strlen(pay))) == NULL) {
296 		warnx("base64buf_url");
297 		goto out;
298 	}
299 
300 	switch (EVP_PKEY_type(pkey->type)) {
301 	case EVP_PKEY_RSA:
302 		alg = "RS256";
303 		evp_md = EVP_sha256();
304 		break;
305 	case EVP_PKEY_EC:
306 		alg = "ES384";
307 		evp_md = EVP_sha384();
308 		break;
309 	default:
310 		warnx("unknown account key type");
311 		goto out;
312 	}
313 
314 	if (op == ACCT_KID_SIGN) {
315 		if ((prot = json_fmt_protected_kid(alg, kid, nonce, url)) ==
316 		    NULL) {
317 			warnx("json_fmt_protected_kid");
318 			goto out;
319 		}
320 	} else {
321 		switch (EVP_PKEY_type(pkey->type)) {
322 		case EVP_PKEY_RSA:
323 			if (!op_sign_rsa(&prot, pkey, nonce, url))
324 				goto out;
325 			break;
326 		case EVP_PKEY_EC:
327 			if (!op_sign_ec(&prot, pkey, nonce, url))
328 				goto out;
329 			break;
330 		default:
331 			warnx("EVP_PKEY_type");
332 			goto out;
333 		}
334 	}
335 
336 	/* The header combined with the nonce, base64. */
337 
338 	if ((prot64 = base64buf_url(prot, strlen(prot))) == NULL) {
339 		warnx("base64buf_url");
340 		goto out;
341 	}
342 
343 	/* Now the signature material. */
344 
345 	cc = asprintf(&sign, "%s.%s", prot64, pay64);
346 	if (cc == -1) {
347 		warn("asprintf");
348 		sign = NULL;
349 		goto out;
350 	}
351 
352 	if ((dig = malloc(EVP_PKEY_size(pkey))) == NULL) {
353 		warn("malloc");
354 		goto out;
355 	}
356 
357 	/*
358 	 * Here we go: using our RSA key as merged into the envelope,
359 	 * sign a SHA256 digest of our message.
360 	 */
361 
362 	if ((ctx = EVP_MD_CTX_new()) == NULL) {
363 		warnx("EVP_MD_CTX_new");
364 		goto out;
365 	} else if (!EVP_SignInit_ex(ctx, evp_md, NULL)) {
366 		warnx("EVP_SignInit_ex");
367 		goto out;
368 	} else if (!EVP_SignUpdate(ctx, sign, strlen(sign))) {
369 		warnx("EVP_SignUpdate");
370 		goto out;
371 	} else if (!EVP_SignFinal(ctx, dig, &digsz, pkey)) {
372 		warnx("EVP_SignFinal");
373 		goto out;
374 	}
375 
376 	switch (EVP_PKEY_type(pkey->type)) {
377 	case EVP_PKEY_RSA:
378 		if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) {
379 			warnx("base64buf_url");
380 			goto out;
381 		}
382 		break;
383 	case EVP_PKEY_EC:
384 		if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
385 			warnx("EVP_PKEY_get0_EC_KEY");
386 			goto out;
387 		}
388 		degree = EC_GROUP_get_degree(EC_KEY_get0_group(ec));
389 		bn_len = (degree + 7) / 8;
390 
391 		digp = dig; /* d2i_ECDSA_SIG advances digp */
392 		if ((ec_sig = d2i_ECDSA_SIG(NULL, &digp, digsz)) == NULL) {
393 			warnx("d2i_ECDSA_SIG");
394 			goto out;
395 		}
396 
397 		ECDSA_SIG_get0(ec_sig, &ec_sig_r, &ec_sig_s);
398 
399 		r_len = BN_num_bytes(ec_sig_r);
400 		s_len = BN_num_bytes(ec_sig_s);
401 
402 		if((r_len > bn_len) || (s_len > bn_len)) {
403 			warnx("ECDSA_SIG_get0");
404 			goto out;
405 		}
406 
407 		bufsz = 2 * bn_len;
408 		if ((buf = calloc(1, bufsz)) == NULL) {
409 			warnx("calloc");
410 			goto out;
411 		}
412 
413 		/* put r and s in with leading zeros if any */
414 		BN_bn2bin(ec_sig_r, buf + bn_len - r_len);
415 		BN_bn2bin(ec_sig_s, buf + bufsz - s_len);
416 
417 		if ((dig64 = base64buf_url((char *)buf, bufsz)) == NULL) {
418 			warnx("base64buf_url");
419 			goto out;
420 		}
421 
422 		break;
423 	default:
424 		warnx("EVP_PKEY_type");
425 		goto out;
426 	}
427 
428 	/*
429 	 * Write back in the correct JSON format.
430 	 * If the reader is closed, just ignore it (we'll pick it up
431 	 * when we next enter the read loop).
432 	 */
433 
434 	if ((fin = json_fmt_signed(prot64, pay64, dig64)) == NULL) {
435 		warnx("json_fmt_signed");
436 		goto out;
437 	} else if (writestr(fd, COMM_REQ, fin) < 0)
438 		goto out;
439 
440 	rc = 1;
441 out:
442 	EVP_MD_CTX_free(ctx);
443 	free(pay);
444 	free(sign);
445 	free(pay64);
446 	free(url);
447 	free(nonce);
448 	free(kid);
449 	free(prot);
450 	free(prot64);
451 	free(dig);
452 	free(dig64);
453 	free(fin);
454 	free(buf);
455 	return rc;
456 }
457 
458 int
459 acctproc(int netsock, const char *acctkey, enum keytype keytype)
460 {
461 	FILE		*f = NULL;
462 	EVP_PKEY	*pkey = NULL;
463 	long		 lval;
464 	enum acctop	 op;
465 	int		 rc = 0, cc, newacct = 0;
466 	mode_t		 prev;
467 
468 	/*
469 	 * First, open our private key file read-only or write-only if
470 	 * we're creating from scratch.
471 	 * Set our umask to be maximally restrictive.
472 	 */
473 
474 	prev = umask((S_IWUSR | S_IXUSR) | S_IRWXG | S_IRWXO);
475 	if ((f = fopen(acctkey, "r")) == NULL && errno == ENOENT) {
476 		f = fopen(acctkey, "wx");
477 		newacct = 1;
478 	}
479 	umask(prev);
480 
481 	if (f == NULL) {
482 		warn("%s", acctkey);
483 		goto out;
484 	}
485 
486 	/* File-system, user, and sandbox jailing. */
487 
488 	ERR_load_crypto_strings();
489 
490 	if (pledge("stdio", NULL) == -1) {
491 		warn("pledge");
492 		goto out;
493 	}
494 
495 	if (newacct) {
496 		switch (keytype) {
497 		case KT_ECDSA:
498 			if ((pkey = ec_key_create(f, acctkey)) == NULL)
499 				goto out;
500 			dodbg("%s: generated ECDSA account key", acctkey);
501 			break;
502 		case KT_RSA:
503 			if ((pkey = rsa_key_create(f, acctkey)) == NULL)
504 				goto out;
505 			dodbg("%s: generated RSA account key", acctkey);
506 			break;
507 		}
508 	} else {
509 		if ((pkey = key_load(f, acctkey)) == NULL)
510 			goto out;
511 		/* XXX check if account key type equals configured key type */
512 		doddbg("%s: loaded account key", acctkey);
513 	}
514 
515 	fclose(f);
516 	f = NULL;
517 
518 	/* Notify the netproc that we've started up. */
519 
520 	if ((cc = writeop(netsock, COMM_ACCT_STAT, ACCT_READY)) == 0)
521 		rc = 1;
522 	if (cc <= 0)
523 		goto out;
524 
525 	/*
526 	 * Now we wait for requests from the network-facing process.
527 	 * It might ask us for our thumbprint, for example, or for us to
528 	 * sign a message.
529 	 */
530 
531 	for (;;) {
532 		op = ACCT__MAX;
533 		if ((lval = readop(netsock, COMM_ACCT)) == 0)
534 			op = ACCT_STOP;
535 		else if (lval == ACCT_SIGN || lval == ACCT_KID_SIGN ||
536 		    lval == ACCT_THUMBPRINT)
537 			op = lval;
538 
539 		if (ACCT__MAX == op) {
540 			warnx("unknown operation from netproc");
541 			goto out;
542 		} else if (ACCT_STOP == op)
543 			break;
544 
545 		switch (op) {
546 		case ACCT_SIGN:
547 		case ACCT_KID_SIGN:
548 			if (op_sign(netsock, pkey, op))
549 				break;
550 			warnx("op_sign");
551 			goto out;
552 		case ACCT_THUMBPRINT:
553 			if (op_thumbprint(netsock, pkey))
554 				break;
555 			warnx("op_thumbprint");
556 			goto out;
557 		default:
558 			abort();
559 		}
560 	}
561 
562 	rc = 1;
563 out:
564 	close(netsock);
565 	if (f != NULL)
566 		fclose(f);
567 	EVP_PKEY_free(pkey);
568 	ERR_print_errors_fp(stderr);
569 	ERR_free_strings();
570 	return rc;
571 }
572