GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/acme-client/keyproc.c Lines: 0 108 0.0 %
Date: 2017-11-07 Branches: 0 62 0.0 %

Line Branch Exec Source
1
/*	$Id: keyproc.c,v 1.9 2017/03/26 18:41:02 deraadt 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/err.h>
28
#include <openssl/rand.h>
29
#include <openssl/x509.h>
30
#include <openssl/x509v3.h>
31
32
#include "extern.h"
33
#include "rsa.h"
34
35
/*
36
 * This was lifted more or less directly from demos/x509/mkreq.c of the
37
 * OpenSSL source code.
38
 */
39
static int
40
add_ext(STACK_OF(X509_EXTENSION) *sk, int nid, const char *value)
41
{
42
	X509_EXTENSION	*ex;
43
	char		*cp;
44
45
	/*
46
	 * XXX: I don't like this at all.
47
	 * There's no documentation for X509V3_EXT_conf_nid, so I'm not
48
	 * sure if the "value" parameter is ever written to, touched,
49
	 * etc.
50
	 * The 'official' examples suggest not (they use a string
51
	 * literal as the input), but to be safe, I'm doing an
52
	 * allocation here and just letting it go.
53
	 * This leaks memory, but bounded to the number of SANs.
54
	 */
55
56
	if ((cp = strdup(value)) == NULL) {
57
		warn("strdup");
58
		return (0);
59
	}
60
	ex = X509V3_EXT_conf_nid(NULL, NULL, nid, cp);
61
	if (ex == NULL) {
62
		warnx("X509V3_EXT_conf_nid");
63
		free(cp);
64
		return (0);
65
	}
66
	sk_X509_EXTENSION_push(sk, ex);
67
	return (1);
68
}
69
70
/*
71
 * Create an X509 certificate from the private key we have on file.
72
 * To do this, we first open the key file, then jail ourselves.
73
 * We then use the crypto library to create the certificate within the
74
 * jail and, on success, ship it to "netsock" as an X509 request.
75
 */
76
int
77
keyproc(int netsock, const char *keyfile,
78
    const char **alts, size_t altsz, int newkey)
79
{
80
	char		*der64 = NULL, *der = NULL, *dercp;
81
	char		*sans = NULL, *san = NULL;
82
	FILE		*f;
83
	size_t		 i, sansz;
84
	void		*pp;
85
	EVP_PKEY	*pkey = NULL;
86
	X509_REQ	*x = NULL;
87
	X509_NAME	*name = NULL;
88
	int		 len, rc = 0, cc, nid;
89
	mode_t		 prev;
90
	STACK_OF(X509_EXTENSION) *exts = NULL;
91
92
	/*
93
	 * First, open our private key file read-only or write-only if
94
	 * we're creating from scratch.
95
	 * Set our umask to be maximally restrictive.
96
	 */
97
98
	prev = umask((S_IWUSR | S_IXUSR) | S_IRWXG | S_IRWXO);
99
	f = fopen(keyfile, newkey ? "wx" : "r");
100
	umask(prev);
101
102
	if (f == NULL) {
103
		warn("%s", keyfile);
104
		goto out;
105
	}
106
107
	/* File-system, user, and sandbox jail. */
108
109
	ERR_load_crypto_strings();
110
111
	if (pledge("stdio flock rpath cpath wpath", NULL) == -1) {
112
		warn("pledge");
113
		goto out;
114
	}
115
116
	if (newkey) {
117
		if ((pkey = rsa_key_create(f, keyfile)) == NULL)
118
			goto out;
119
		dodbg("%s: generated RSA domain key", keyfile);
120
	} else {
121
		if ((pkey = rsa_key_load(f, keyfile)) == NULL)
122
			goto out;
123
		doddbg("%s: loaded RSA domain key", keyfile);
124
	}
125
126
	fclose(f);
127
	f = NULL;
128
129
	/*
130
	 * Generate our certificate from the EVP public key.
131
	 * Then set it as the X509 requester's key.
132
	 */
133
134
	if ((x = X509_REQ_new()) == NULL) {
135
		warnx("X509_new");
136
		goto out;
137
	} else if (!X509_REQ_set_pubkey(x, pkey)) {
138
		warnx("X509_set_pubkey");
139
		goto out;
140
	}
141
142
	/* Now specify the common name that we'll request. */
143
144
	if ((name = X509_NAME_new()) == NULL) {
145
		warnx("X509_NAME_new");
146
		goto out;
147
	} else if (!X509_NAME_add_entry_by_txt(name, "CN",
148
		MBSTRING_ASC, (u_char *)alts[0], -1, -1, 0)) {
149
		warnx("X509_NAME_add_entry_by_txt: CN=%s", alts[0]);
150
		goto out;
151
	} else if (!X509_REQ_set_subject_name(x, name)) {
152
		warnx("X509_req_set_issuer_name");
153
		goto out;
154
	}
155
156
	/*
157
	 * Now add the SAN extensions.
158
	 * This was lifted more or less directly from demos/x509/mkreq.c
159
	 * of the OpenSSL source code.
160
	 * (The zeroth altname is the domain name.)
161
	 * TODO: is this the best way of doing this?
162
	 */
163
164
	if (altsz > 1) {
165
		nid = NID_subject_alt_name;
166
		if ((exts = sk_X509_EXTENSION_new_null()) == NULL) {
167
			warnx("sk_X509_EXTENSION_new_null");
168
			goto out;
169
		}
170
		/* Initialise to empty string. */
171
		if ((sans = strdup("")) == NULL) {
172
			warn("strdup");
173
			goto out;
174
		}
175
		sansz = strlen(sans) + 1;
176
177
		/*
178
		 * For each SAN entry, append it to the string.
179
		 * We need a single SAN entry for all of the SAN
180
		 * domains: NOT an entry per domain!
181
		 */
182
183
		for (i = 1; i < altsz; i++) {
184
			cc = asprintf(&san, "%sDNS:%s",
185
			    i > 1 ? "," : "", alts[i]);
186
			if (cc == -1) {
187
				warn("asprintf");
188
				goto out;
189
			}
190
			pp = recallocarray(sans, sansz, sansz + strlen(san), 1);
191
			if (pp == NULL) {
192
				warn("recallocarray");
193
				goto out;
194
			}
195
			sans = pp;
196
			sansz += strlen(san);
197
			strlcat(sans, san, sansz);
198
			free(san);
199
			san = NULL;
200
		}
201
202
		if (!add_ext(exts, nid, sans)) {
203
			warnx("add_ext");
204
			goto out;
205
		} else if (!X509_REQ_add_extensions(x, exts)) {
206
			warnx("X509_REQ_add_extensions");
207
			goto out;
208
		}
209
		sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
210
	}
211
212
	/* Sign the X509 request using SHA256. */
213
214
	if (!X509_REQ_sign(x, pkey, EVP_sha256())) {
215
		warnx("X509_sign");
216
		goto out;
217
	}
218
219
	/* Now, serialise to DER, then base64. */
220
221
	if ((len = i2d_X509_REQ(x, NULL)) < 0) {
222
		warnx("i2d_X509");
223
		goto out;
224
	} else if ((der = dercp = malloc(len)) == NULL) {
225
		warn("malloc");
226
		goto out;
227
	} else if (len != i2d_X509_REQ(x, (u_char **)&dercp)) {
228
		warnx("i2d_X509");
229
		goto out;
230
	} else if ((der64 = base64buf_url(der, len)) == NULL) {
231
		warnx("base64buf_url");
232
		goto out;
233
	}
234
235
	/*
236
	 * Write that we're ready, then write.
237
	 * We ignore reader-closed failure, as we're just going to roll
238
	 * into the exit case anyway.
239
	 */
240
241
	if (writeop(netsock, COMM_KEY_STAT, KEY_READY) < 0)
242
		goto out;
243
	if (writestr(netsock, COMM_CERT, der64) < 0)
244
		goto out;
245
246
	rc = 1;
247
out:
248
	close(netsock);
249
	if (f != NULL)
250
		fclose(f);
251
	free(der);
252
	free(der64);
253
	free(sans);
254
	free(san);
255
	if (x != NULL)
256
		X509_REQ_free(x);
257
	if (name != NULL)
258
		X509_NAME_free(name);
259
	if (pkey != NULL)
260
		EVP_PKEY_free(pkey);
261
	ERR_print_errors_fp(stderr);
262
	ERR_free_strings();
263
	return (rc);
264
}