xref: /openbsd-src/usr.sbin/acme-client/acctproc.c (revision d1df930ffab53da22f3324c32bed7ac5709915e6)
1 /*	$Id: acctproc.c,v 1.12 2018/07/28 15:25:23 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/rsa.h>
28 #include <openssl/rand.h>
29 #include <openssl/err.h>
30 
31 #include "extern.h"
32 #include "rsa.h"
33 
34 /*
35  * Converts a BIGNUM to the form used in JWK.
36  * This is essentially a base64-encoded big-endian binary string
37  * representation of the number.
38  */
39 static char *
40 bn2string(const BIGNUM *bn)
41 {
42 	int	 len;
43 	char	*buf, *bbuf;
44 
45 	/* Extract big-endian representation of BIGNUM. */
46 
47 	len = BN_num_bytes(bn);
48 	if ((buf = malloc(len)) == NULL) {
49 		warn("malloc");
50 		return NULL;
51 	} else if (len != BN_bn2bin(bn, (unsigned char *)buf)) {
52 		warnx("BN_bn2bin");
53 		free(buf);
54 		return NULL;
55 	}
56 
57 	/* Convert to base64url. */
58 
59 	if ((bbuf = base64buf_url(buf, len)) == NULL) {
60 		warnx("base64buf_url");
61 		free(buf);
62 		return NULL;
63 	}
64 
65 	free(buf);
66 	return bbuf;
67 }
68 
69 /*
70  * Extract the relevant RSA components from the key and create the JSON
71  * thumbprint from them.
72  */
73 static char *
74 op_thumb_rsa(EVP_PKEY *pkey)
75 {
76 	char	*exp = NULL, *mod = NULL, *json = NULL;
77 	RSA	*r;
78 
79 	if ((r = EVP_PKEY_get1_RSA(pkey)) == NULL)
80 		warnx("EVP_PKEY_get1_RSA");
81 	else if ((mod = bn2string(r->n)) == NULL)
82 		warnx("bn2string");
83 	else if ((exp = bn2string(r->e)) == NULL)
84 		warnx("bn2string");
85 	else if ((json = json_fmt_thumb_rsa(exp, mod)) == NULL)
86 		warnx("json_fmt_thumb_rsa");
87 
88 	free(exp);
89 	free(mod);
90 	return json;
91 }
92 
93 /*
94  * The thumbprint operation is used for the challenge sequence.
95  */
96 static int
97 op_thumbprint(int fd, EVP_PKEY *pkey)
98 {
99 	char		*thumb = NULL, *dig64 = NULL;
100 	EVP_MD_CTX	*ctx = NULL;
101 	unsigned char	*dig = NULL;
102 	unsigned int	 digsz;
103 	int		 rc = 0;
104 
105 	/* Construct the thumbprint input itself. */
106 
107 	switch (EVP_PKEY_type(pkey->type)) {
108 	case EVP_PKEY_RSA:
109 		if ((thumb = op_thumb_rsa(pkey)) != NULL)
110 			break;
111 		goto out;
112 	default:
113 		warnx("EVP_PKEY_type: unknown key type");
114 		goto out;
115 	}
116 
117 	/*
118 	 * Compute the SHA256 digest of the thumbprint then
119 	 * base64-encode the digest itself.
120 	 * If the reader is closed when we write, ignore it (we'll pick
121 	 * it up in the read loop).
122 	 */
123 
124 	if ((dig = malloc(EVP_MAX_MD_SIZE)) == NULL) {
125 		warn("malloc");
126 		goto out;
127 	} else if ((ctx = EVP_MD_CTX_create()) == NULL) {
128 		warnx("EVP_MD_CTX_create");
129 		goto out;
130 	} else if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) {
131 		warnx("EVP_SignInit_ex");
132 		goto out;
133 	} else if (!EVP_DigestUpdate(ctx, thumb, strlen(thumb))) {
134 		warnx("EVP_SignUpdate");
135 		goto out;
136 	} else if (!EVP_DigestFinal_ex(ctx, dig, &digsz)) {
137 		warnx("EVP_SignFinal");
138 		goto out;
139 	} else if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) {
140 		warnx("base64buf_url");
141 		goto out;
142 	} else if (writestr(fd, COMM_THUMB, dig64) < 0)
143 		goto out;
144 
145 	rc = 1;
146 out:
147 	if (ctx != NULL)
148 		EVP_MD_CTX_destroy(ctx);
149 
150 	free(thumb);
151 	free(dig);
152 	free(dig64);
153 	return rc;
154 }
155 
156 static int
157 op_sign_rsa(char **head, char **prot, EVP_PKEY *pkey, const char *nonce)
158 {
159 	char	*exp = NULL, *mod = NULL;
160 	int	rc = 0;
161 	RSA	*r;
162 
163 	*head = NULL;
164 	*prot = NULL;
165 
166 	/*
167 	 * First, extract relevant portions of our private key.
168 	 * Then construct the public header.
169 	 * Finally, format the header combined with the nonce.
170 	 */
171 
172 	if ((r = EVP_PKEY_get1_RSA(pkey)) == NULL)
173 		warnx("EVP_PKEY_get1_RSA");
174 	else if ((mod = bn2string(r->n)) == NULL)
175 		warnx("bn2string");
176 	else if ((exp = bn2string(r->e)) == NULL)
177 		warnx("bn2string");
178 	else if ((*head = json_fmt_header_rsa(exp, mod)) == NULL)
179 		warnx("json_fmt_header_rsa");
180 	else if ((*prot = json_fmt_protected_rsa(exp, mod, nonce)) == NULL)
181 		warnx("json_fmt_protected_rsa");
182 	else
183 		rc = 1;
184 
185 	free(exp);
186 	free(mod);
187 	return rc;
188 }
189 
190 /*
191  * Operation to sign a message with the account key.
192  * This requires the sender ("fd") to provide the payload and a nonce.
193  */
194 static int
195 op_sign(int fd, EVP_PKEY *pkey)
196 {
197 	char		*nonce = NULL, *pay = NULL, *pay64 = NULL;
198 	char		*prot = NULL, *prot64 = NULL, *head = NULL;
199 	char		*sign = NULL, *dig64 = NULL, *fin = NULL;
200 	unsigned char	*dig = NULL;
201 	EVP_MD_CTX	*ctx = NULL;
202 	int		 cc, rc = 0;
203 	unsigned int	 digsz;
204 
205 	/* Read our payload and nonce from the requestor. */
206 
207 	if ((pay = readstr(fd, COMM_PAY)) == NULL)
208 		goto out;
209 	else if ((nonce = readstr(fd, COMM_NONCE)) == NULL)
210 		goto out;
211 
212 	/* Base64-encode the payload. */
213 
214 	if ((pay64 = base64buf_url(pay, strlen(pay))) == NULL) {
215 		warnx("base64buf_url");
216 		goto out;
217 	}
218 
219 	switch (EVP_PKEY_type(pkey->type)) {
220 	case EVP_PKEY_RSA:
221 		if (!op_sign_rsa(&head, &prot, pkey, nonce))
222 			goto out;
223 		break;
224 	default:
225 		warnx("EVP_PKEY_type");
226 		goto out;
227 	}
228 
229 	/* The header combined with the nonce, base64. */
230 
231 	if ((prot64 = base64buf_url(prot, strlen(prot))) == NULL) {
232 		warnx("base64buf_url");
233 		goto out;
234 	}
235 
236 	/* Now the signature material. */
237 
238 	cc = asprintf(&sign, "%s.%s", prot64, pay64);
239 	if (cc == -1) {
240 		warn("asprintf");
241 		sign = NULL;
242 		goto out;
243 	}
244 
245 	if ((dig = malloc(EVP_PKEY_size(pkey))) == NULL) {
246 		warn("malloc");
247 		goto out;
248 	}
249 
250 	/*
251 	 * Here we go: using our RSA key as merged into the envelope,
252 	 * sign a SHA256 digest of our message.
253 	 */
254 
255 	if ((ctx = EVP_MD_CTX_create()) == NULL) {
256 		warnx("EVP_MD_CTX_create");
257 		goto out;
258 	} else if (!EVP_SignInit_ex(ctx, EVP_sha256(), NULL)) {
259 		warnx("EVP_SignInit_ex");
260 		goto out;
261 	} else if (!EVP_SignUpdate(ctx, sign, strlen(sign))) {
262 		warnx("EVP_SignUpdate");
263 		goto out;
264 	} else if (!EVP_SignFinal(ctx, dig, &digsz, pkey)) {
265 		warnx("EVP_SignFinal");
266 		goto out;
267 	} else if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) {
268 		warnx("base64buf_url");
269 		goto out;
270 	}
271 
272 	/*
273 	 * Write back in the correct JSON format.
274 	 * If the reader is closed, just ignore it (we'll pick it up
275 	 * when we next enter the read loop).
276 	 */
277 
278 	if ((fin = json_fmt_signed(head, prot64, pay64, dig64)) == NULL) {
279 		warnx("json_fmt_signed");
280 		goto out;
281 	} else if (writestr(fd, COMM_REQ, fin) < 0)
282 		goto out;
283 
284 	rc = 1;
285 out:
286 	if (ctx != NULL)
287 		EVP_MD_CTX_destroy(ctx);
288 
289 	free(pay);
290 	free(sign);
291 	free(pay64);
292 	free(nonce);
293 	free(head);
294 	free(prot);
295 	free(prot64);
296 	free(dig);
297 	free(dig64);
298 	free(fin);
299 	return rc;
300 }
301 
302 int
303 acctproc(int netsock, const char *acctkey, int newacct)
304 {
305 	FILE		*f = NULL;
306 	EVP_PKEY	*pkey = NULL;
307 	long		 lval;
308 	enum acctop	 op;
309 	int		 rc = 0, cc;
310 	mode_t		 prev;
311 
312 	/*
313 	 * First, open our private key file read-only or write-only if
314 	 * we're creating from scratch.
315 	 * Set our umask to be maximally restrictive.
316 	 */
317 
318 	prev = umask((S_IWUSR | S_IXUSR) | S_IRWXG | S_IRWXO);
319 	f = fopen(acctkey, newacct ? "wx" : "r");
320 	umask(prev);
321 
322 	if (f == NULL) {
323 		warn("%s", acctkey);
324 		goto out;
325 	}
326 
327 	/* File-system, user, and sandbox jailing. */
328 
329 	ERR_load_crypto_strings();
330 
331 	if (pledge("stdio", NULL) == -1) {
332 		warn("pledge");
333 		goto out;
334 	}
335 
336 	if (newacct) {
337 		if ((pkey = rsa_key_create(f, acctkey)) == NULL)
338 			goto out;
339 		dodbg("%s: generated RSA account key", acctkey);
340 	} else {
341 		if ((pkey = rsa_key_load(f, acctkey)) == NULL)
342 			goto out;
343 		doddbg("%s: loaded RSA account key", acctkey);
344 	}
345 
346 	fclose(f);
347 	f = NULL;
348 
349 	/* Notify the netproc that we've started up. */
350 
351 	if ((cc = writeop(netsock, COMM_ACCT_STAT, ACCT_READY)) == 0)
352 		rc = 1;
353 	if (cc <= 0)
354 		goto out;
355 
356 	/*
357 	 * Now we wait for requests from the network-facing process.
358 	 * It might ask us for our thumbprint, for example, or for us to
359 	 * sign a message.
360 	 */
361 
362 	for (;;) {
363 		op = ACCT__MAX;
364 		if ((lval = readop(netsock, COMM_ACCT)) == 0)
365 			op = ACCT_STOP;
366 		else if (lval == ACCT_SIGN || lval == ACCT_THUMBPRINT)
367 			op = lval;
368 
369 		if (ACCT__MAX == op) {
370 			warnx("unknown operation from netproc");
371 			goto out;
372 		} else if (ACCT_STOP == op)
373 			break;
374 
375 		switch (op) {
376 		case ACCT_SIGN:
377 			if (op_sign(netsock, pkey))
378 				break;
379 			warnx("op_sign");
380 			goto out;
381 		case ACCT_THUMBPRINT:
382 			if (op_thumbprint(netsock, pkey))
383 				break;
384 			warnx("op_thumbprint");
385 			goto out;
386 		default:
387 			abort();
388 		}
389 	}
390 
391 	rc = 1;
392 out:
393 	close(netsock);
394 	if (f != NULL)
395 		fclose(f);
396 	EVP_PKEY_free(pkey);
397 	ERR_print_errors_fp(stderr);
398 	ERR_free_strings();
399 	return rc;
400 }
401