xref: /openbsd-src/usr.sbin/acme-client/keyproc.c (revision df5860abc566e502315ab98e6881248d5a0ff23a)
1*df5860abStb /*	$Id: keyproc.c,v 1.18 2022/08/28 18:30:29 tb 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>
21de579d12Sflorian #include <stdio.h>
22de579d12Sflorian #include <stdlib.h>
23de579d12Sflorian #include <string.h>
24de579d12Sflorian #include <unistd.h>
25de579d12Sflorian 
26de579d12Sflorian #include <openssl/pem.h>
27de579d12Sflorian #include <openssl/err.h>
28de579d12Sflorian #include <openssl/rand.h>
29de579d12Sflorian #include <openssl/x509.h>
30de579d12Sflorian #include <openssl/x509v3.h>
31de579d12Sflorian 
32de579d12Sflorian #include "extern.h"
333e86e78bSgilles #include "key.h"
34de579d12Sflorian 
35de579d12Sflorian /*
36de579d12Sflorian  * This was lifted more or less directly from demos/x509/mkreq.c of the
37de579d12Sflorian  * OpenSSL source code.
38de579d12Sflorian  */
39de579d12Sflorian static int
add_ext(STACK_OF (X509_EXTENSION)* sk,int nid,const char * value)40de579d12Sflorian add_ext(STACK_OF(X509_EXTENSION) *sk, int nid, const char *value)
41de579d12Sflorian {
42de579d12Sflorian 	X509_EXTENSION	*ex;
43de579d12Sflorian 	char		*cp;
44de579d12Sflorian 
45de579d12Sflorian 	/*
46de579d12Sflorian 	 * XXX: I don't like this at all.
47de579d12Sflorian 	 * There's no documentation for X509V3_EXT_conf_nid, so I'm not
48de579d12Sflorian 	 * sure if the "value" parameter is ever written to, touched,
49de579d12Sflorian 	 * etc.
50de579d12Sflorian 	 * The 'official' examples suggest not (they use a string
51de579d12Sflorian 	 * literal as the input), but to be safe, I'm doing an
52de579d12Sflorian 	 * allocation here and just letting it go.
53de579d12Sflorian 	 * This leaks memory, but bounded to the number of SANs.
54de579d12Sflorian 	 */
55de579d12Sflorian 
564de82fa3Sderaadt 	if ((cp = strdup(value)) == NULL) {
57de579d12Sflorian 		warn("strdup");
58de579d12Sflorian 		return (0);
59de579d12Sflorian 	}
60de579d12Sflorian 	ex = X509V3_EXT_conf_nid(NULL, NULL, nid, cp);
614de82fa3Sderaadt 	if (ex == NULL) {
62de579d12Sflorian 		warnx("X509V3_EXT_conf_nid");
63de579d12Sflorian 		free(cp);
64de579d12Sflorian 		return (0);
65de579d12Sflorian 	}
66de579d12Sflorian 	sk_X509_EXTENSION_push(sk, ex);
67de579d12Sflorian 	return (1);
68de579d12Sflorian }
69de579d12Sflorian 
70de579d12Sflorian /*
71de579d12Sflorian  * Create an X509 certificate from the private key we have on file.
72de579d12Sflorian  * To do this, we first open the key file, then jail ourselves.
73de579d12Sflorian  * We then use the crypto library to create the certificate within the
74de579d12Sflorian  * jail and, on success, ship it to "netsock" as an X509 request.
75de579d12Sflorian  */
76de579d12Sflorian int
keyproc(int netsock,const char * keyfile,const char ** alts,size_t altsz,enum keytype keytype)7765a104faSflorian keyproc(int netsock, const char *keyfile, const char **alts, size_t altsz,
7865a104faSflorian     enum keytype keytype)
79de579d12Sflorian {
807bce6888Sderaadt 	char		*der64 = NULL, *der = NULL, *dercp;
817bce6888Sderaadt 	char		*sans = NULL, *san = NULL;
82de579d12Sflorian 	FILE		*f;
83de579d12Sflorian 	size_t		 i, sansz;
84de579d12Sflorian 	void		*pp;
857bce6888Sderaadt 	EVP_PKEY	*pkey = NULL;
867bce6888Sderaadt 	X509_REQ	*x = NULL;
877bce6888Sderaadt 	X509_NAME	*name = NULL;
882570ecd0Sflorian 	int		 len, rc = 0, cc, nid, newkey = 0;
89de579d12Sflorian 	mode_t		 prev;
907bce6888Sderaadt 	STACK_OF(X509_EXTENSION) *exts = NULL;
91de579d12Sflorian 
92de579d12Sflorian 	/*
93de579d12Sflorian 	 * First, open our private key file read-only or write-only if
94de579d12Sflorian 	 * we're creating from scratch.
95de579d12Sflorian 	 * Set our umask to be maximally restrictive.
96de579d12Sflorian 	 */
97de579d12Sflorian 
98de579d12Sflorian 	prev = umask((S_IWUSR | S_IXUSR) | S_IRWXG | S_IRWXO);
992570ecd0Sflorian 	if ((f = fopen(keyfile, "r")) == NULL && errno == ENOENT) {
1002570ecd0Sflorian 		f = fopen(keyfile, "wx");
1012570ecd0Sflorian 		newkey = 1;
1022570ecd0Sflorian 	}
103de579d12Sflorian 	umask(prev);
104de579d12Sflorian 
1054de82fa3Sderaadt 	if (f == NULL) {
106de579d12Sflorian 		warn("%s", keyfile);
107de579d12Sflorian 		goto out;
108de579d12Sflorian 	}
109de579d12Sflorian 
110de579d12Sflorian 	/* File-system, user, and sandbox jail. */
111de579d12Sflorian 
112de579d12Sflorian 	ERR_load_crypto_strings();
113de579d12Sflorian 
114ec0d8c8bSderaadt 	if (pledge("stdio", NULL) == -1) {
115ec0d8c8bSderaadt 		warn("pledge");
116de579d12Sflorian 		goto out;
117ec0d8c8bSderaadt 	}
118de579d12Sflorian 
119de579d12Sflorian 	if (newkey) {
12065a104faSflorian 		switch (keytype) {
12165a104faSflorian 		case KT_ECDSA:
1223e86e78bSgilles 			if ((pkey = ec_key_create(f, keyfile)) == NULL)
1233e86e78bSgilles 				goto out;
1243e86e78bSgilles 			dodbg("%s: generated ECDSA domain key", keyfile);
12565a104faSflorian 			break;
12665a104faSflorian 		case KT_RSA:
1274de82fa3Sderaadt 			if ((pkey = rsa_key_create(f, keyfile)) == NULL)
128de579d12Sflorian 				goto out;
129de579d12Sflorian 			dodbg("%s: generated RSA domain key", keyfile);
13065a104faSflorian 			break;
1313e86e78bSgilles 		}
132de579d12Sflorian 	} else {
1333e86e78bSgilles 		if ((pkey = key_load(f, keyfile)) == NULL)
134de579d12Sflorian 			goto out;
135ffbdc72dSflorian 		/* XXX check if domain key type equals configured key type */
1363e86e78bSgilles 		doddbg("%s: loaded domain key", keyfile);
137de579d12Sflorian 	}
138de579d12Sflorian 
139de579d12Sflorian 	fclose(f);
140de579d12Sflorian 	f = NULL;
141de579d12Sflorian 
142de579d12Sflorian 	/*
143de579d12Sflorian 	 * Generate our certificate from the EVP public key.
144de579d12Sflorian 	 * Then set it as the X509 requester's key.
145de579d12Sflorian 	 */
146de579d12Sflorian 
1474de82fa3Sderaadt 	if ((x = X509_REQ_new()) == NULL) {
148*df5860abStb 		warnx("X509_REQ_new");
149de579d12Sflorian 		goto out;
150a4dae718Sjsing 	} else if (!X509_REQ_set_version(x, 0)) {
151*df5860abStb 		warnx("X509_REQ_set_version");
152a4dae718Sjsing 		goto out;
153de579d12Sflorian 	} else if (!X509_REQ_set_pubkey(x, pkey)) {
154*df5860abStb 		warnx("X509_REQ_set_pubkey");
155de579d12Sflorian 		goto out;
156de579d12Sflorian 	}
157de579d12Sflorian 
158de579d12Sflorian 	/* Now specify the common name that we'll request. */
159de579d12Sflorian 
1604de82fa3Sderaadt 	if ((name = X509_NAME_new()) == NULL) {
161de579d12Sflorian 		warnx("X509_NAME_new");
162de579d12Sflorian 		goto out;
163de579d12Sflorian 	} else if (!X509_NAME_add_entry_by_txt(name, "CN",
164de579d12Sflorian 		MBSTRING_ASC, (u_char *)alts[0], -1, -1, 0)) {
165de579d12Sflorian 		warnx("X509_NAME_add_entry_by_txt: CN=%s", alts[0]);
166de579d12Sflorian 		goto out;
167de579d12Sflorian 	} else if (!X509_REQ_set_subject_name(x, name)) {
168de579d12Sflorian 		warnx("X509_req_set_issuer_name");
169de579d12Sflorian 		goto out;
170de579d12Sflorian 	}
171de579d12Sflorian 
172de579d12Sflorian 	/*
173de579d12Sflorian 	 * Now add the SAN extensions.
174de579d12Sflorian 	 * This was lifted more or less directly from demos/x509/mkreq.c
175de579d12Sflorian 	 * of the OpenSSL source code.
176de579d12Sflorian 	 * (The zeroth altname is the domain name.)
177de579d12Sflorian 	 * TODO: is this the best way of doing this?
178de579d12Sflorian 	 */
179de579d12Sflorian 
180de579d12Sflorian 	nid = NID_subject_alt_name;
1814de82fa3Sderaadt 	if ((exts = sk_X509_EXTENSION_new_null()) == NULL) {
182de579d12Sflorian 		warnx("sk_X509_EXTENSION_new_null");
183de579d12Sflorian 		goto out;
184de579d12Sflorian 	}
185de579d12Sflorian 	/* Initialise to empty string. */
1864de82fa3Sderaadt 	if ((sans = strdup("")) == NULL) {
187de579d12Sflorian 		warn("strdup");
188de579d12Sflorian 		goto out;
189de579d12Sflorian 	}
190de579d12Sflorian 	sansz = strlen(sans) + 1;
191de579d12Sflorian 
192de579d12Sflorian 	/*
193de579d12Sflorian 	 * For each SAN entry, append it to the string.
194de579d12Sflorian 	 * We need a single SAN entry for all of the SAN
195de579d12Sflorian 	 * domains: NOT an entry per domain!
196de579d12Sflorian 	 */
197de579d12Sflorian 
19871c23e2aSsthen 	for (i = 0; i < altsz; i++) {
199de579d12Sflorian 		cc = asprintf(&san, "%sDNS:%s",
20071c23e2aSsthen 		    i ? "," : "", alts[i]);
2014de82fa3Sderaadt 		if (cc == -1) {
202de579d12Sflorian 			warn("asprintf");
203de579d12Sflorian 			goto out;
204de579d12Sflorian 		}
205425fd4bbSderaadt 		pp = recallocarray(sans, sansz, sansz + strlen(san), 1);
2064de82fa3Sderaadt 		if (pp == NULL) {
207425fd4bbSderaadt 			warn("recallocarray");
208de579d12Sflorian 			goto out;
209de579d12Sflorian 		}
210de579d12Sflorian 		sans = pp;
211de579d12Sflorian 		sansz += strlen(san);
212de579d12Sflorian 		strlcat(sans, san, sansz);
213de579d12Sflorian 		free(san);
214de579d12Sflorian 		san = NULL;
215de579d12Sflorian 	}
216de579d12Sflorian 
217de579d12Sflorian 	if (!add_ext(exts, nid, sans)) {
218de579d12Sflorian 		warnx("add_ext");
219de579d12Sflorian 		goto out;
220de579d12Sflorian 	} else if (!X509_REQ_add_extensions(x, exts)) {
221de579d12Sflorian 		warnx("X509_REQ_add_extensions");
222de579d12Sflorian 		goto out;
223de579d12Sflorian 	}
22455599e87Sderaadt 	sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
225de579d12Sflorian 
226de579d12Sflorian 	/* Sign the X509 request using SHA256. */
227de579d12Sflorian 
228de579d12Sflorian 	if (!X509_REQ_sign(x, pkey, EVP_sha256())) {
229de579d12Sflorian 		warnx("X509_sign");
230de579d12Sflorian 		goto out;
231de579d12Sflorian 	}
232de579d12Sflorian 
233de579d12Sflorian 	/* Now, serialise to DER, then base64. */
234de579d12Sflorian 
235de579d12Sflorian 	if ((len = i2d_X509_REQ(x, NULL)) < 0) {
236*df5860abStb 		warnx("i2d_X509_REQ");
237de579d12Sflorian 		goto out;
2384de82fa3Sderaadt 	} else if ((der = dercp = malloc(len)) == NULL) {
239de579d12Sflorian 		warn("malloc");
240de579d12Sflorian 		goto out;
241de579d12Sflorian 	} else if (len != i2d_X509_REQ(x, (u_char **)&dercp)) {
242*df5860abStb 		warnx("i2d_X509_REQ");
243de579d12Sflorian 		goto out;
2444de82fa3Sderaadt 	} else if ((der64 = base64buf_url(der, len)) == NULL) {
245de579d12Sflorian 		warnx("base64buf_url");
246de579d12Sflorian 		goto out;
247de579d12Sflorian 	}
248de579d12Sflorian 
249de579d12Sflorian 	/*
250de579d12Sflorian 	 * Write that we're ready, then write.
251de579d12Sflorian 	 * We ignore reader-closed failure, as we're just going to roll
252de579d12Sflorian 	 * into the exit case anyway.
253de579d12Sflorian 	 */
254de579d12Sflorian 
255de579d12Sflorian 	if (writeop(netsock, COMM_KEY_STAT, KEY_READY) < 0)
256de579d12Sflorian 		goto out;
257de579d12Sflorian 	if (writestr(netsock, COMM_CERT, der64) < 0)
258de579d12Sflorian 		goto out;
259de579d12Sflorian 
260de579d12Sflorian 	rc = 1;
261de579d12Sflorian out:
262de579d12Sflorian 	close(netsock);
2634de82fa3Sderaadt 	if (f != NULL)
264de579d12Sflorian 		fclose(f);
265de579d12Sflorian 	free(der);
266de579d12Sflorian 	free(der64);
267de579d12Sflorian 	free(sans);
268de579d12Sflorian 	free(san);
269de579d12Sflorian 	X509_REQ_free(x);
270de579d12Sflorian 	X509_NAME_free(name);
271de579d12Sflorian 	EVP_PKEY_free(pkey);
272de579d12Sflorian 	ERR_print_errors_fp(stderr);
273de579d12Sflorian 	ERR_free_strings();
274e1bd26f6Stb 	return rc;
275de579d12Sflorian }
276