GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/acme-client/acctproc.c Lines: 0 173 0.0 %
Date: 2017-11-13 Branches: 0 109 0.0 %

Line Branch Exec Source
1
/*	$Id: acctproc.c,v 1.11 2017/01/24 13:32:55 jsing 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 flock rpath cpath wpath", 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
	if (pkey != NULL)
397
		EVP_PKEY_free(pkey);
398
	ERR_print_errors_fp(stderr);
399
	ERR_free_strings();
400
	return rc;
401
}