1 /* $Id: keyproc.c,v 1.18 2022/08/28 18:30:29 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/err.h> 28 #include <openssl/rand.h> 29 #include <openssl/x509.h> 30 #include <openssl/x509v3.h> 31 32 #include "extern.h" 33 #include "key.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, const char **alts, size_t altsz, 78 enum keytype keytype) 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, newkey = 0; 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 if ((f = fopen(keyfile, "r")) == NULL && errno == ENOENT) { 100 f = fopen(keyfile, "wx"); 101 newkey = 1; 102 } 103 umask(prev); 104 105 if (f == NULL) { 106 warn("%s", keyfile); 107 goto out; 108 } 109 110 /* File-system, user, and sandbox jail. */ 111 112 ERR_load_crypto_strings(); 113 114 if (pledge("stdio", NULL) == -1) { 115 warn("pledge"); 116 goto out; 117 } 118 119 if (newkey) { 120 switch (keytype) { 121 case KT_ECDSA: 122 if ((pkey = ec_key_create(f, keyfile)) == NULL) 123 goto out; 124 dodbg("%s: generated ECDSA domain key", keyfile); 125 break; 126 case KT_RSA: 127 if ((pkey = rsa_key_create(f, keyfile)) == NULL) 128 goto out; 129 dodbg("%s: generated RSA domain key", keyfile); 130 break; 131 } 132 } else { 133 if ((pkey = key_load(f, keyfile)) == NULL) 134 goto out; 135 /* XXX check if domain key type equals configured key type */ 136 doddbg("%s: loaded domain key", keyfile); 137 } 138 139 fclose(f); 140 f = NULL; 141 142 /* 143 * Generate our certificate from the EVP public key. 144 * Then set it as the X509 requester's key. 145 */ 146 147 if ((x = X509_REQ_new()) == NULL) { 148 warnx("X509_REQ_new"); 149 goto out; 150 } else if (!X509_REQ_set_version(x, 0)) { 151 warnx("X509_REQ_set_version"); 152 goto out; 153 } else if (!X509_REQ_set_pubkey(x, pkey)) { 154 warnx("X509_REQ_set_pubkey"); 155 goto out; 156 } 157 158 /* Now specify the common name that we'll request. */ 159 160 if ((name = X509_NAME_new()) == NULL) { 161 warnx("X509_NAME_new"); 162 goto out; 163 } else if (!X509_NAME_add_entry_by_txt(name, "CN", 164 MBSTRING_ASC, (u_char *)alts[0], -1, -1, 0)) { 165 warnx("X509_NAME_add_entry_by_txt: CN=%s", alts[0]); 166 goto out; 167 } else if (!X509_REQ_set_subject_name(x, name)) { 168 warnx("X509_req_set_issuer_name"); 169 goto out; 170 } 171 172 /* 173 * Now add the SAN extensions. 174 * This was lifted more or less directly from demos/x509/mkreq.c 175 * of the OpenSSL source code. 176 * (The zeroth altname is the domain name.) 177 * TODO: is this the best way of doing this? 178 */ 179 180 nid = NID_subject_alt_name; 181 if ((exts = sk_X509_EXTENSION_new_null()) == NULL) { 182 warnx("sk_X509_EXTENSION_new_null"); 183 goto out; 184 } 185 /* Initialise to empty string. */ 186 if ((sans = strdup("")) == NULL) { 187 warn("strdup"); 188 goto out; 189 } 190 sansz = strlen(sans) + 1; 191 192 /* 193 * For each SAN entry, append it to the string. 194 * We need a single SAN entry for all of the SAN 195 * domains: NOT an entry per domain! 196 */ 197 198 for (i = 0; i < altsz; i++) { 199 cc = asprintf(&san, "%sDNS:%s", 200 i ? "," : "", alts[i]); 201 if (cc == -1) { 202 warn("asprintf"); 203 goto out; 204 } 205 pp = recallocarray(sans, sansz, sansz + strlen(san), 1); 206 if (pp == NULL) { 207 warn("recallocarray"); 208 goto out; 209 } 210 sans = pp; 211 sansz += strlen(san); 212 strlcat(sans, san, sansz); 213 free(san); 214 san = NULL; 215 } 216 217 if (!add_ext(exts, nid, sans)) { 218 warnx("add_ext"); 219 goto out; 220 } else if (!X509_REQ_add_extensions(x, exts)) { 221 warnx("X509_REQ_add_extensions"); 222 goto out; 223 } 224 sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); 225 226 /* Sign the X509 request using SHA256. */ 227 228 if (!X509_REQ_sign(x, pkey, EVP_sha256())) { 229 warnx("X509_sign"); 230 goto out; 231 } 232 233 /* Now, serialise to DER, then base64. */ 234 235 if ((len = i2d_X509_REQ(x, NULL)) < 0) { 236 warnx("i2d_X509_REQ"); 237 goto out; 238 } else if ((der = dercp = malloc(len)) == NULL) { 239 warn("malloc"); 240 goto out; 241 } else if (len != i2d_X509_REQ(x, (u_char **)&dercp)) { 242 warnx("i2d_X509_REQ"); 243 goto out; 244 } else if ((der64 = base64buf_url(der, len)) == NULL) { 245 warnx("base64buf_url"); 246 goto out; 247 } 248 249 /* 250 * Write that we're ready, then write. 251 * We ignore reader-closed failure, as we're just going to roll 252 * into the exit case anyway. 253 */ 254 255 if (writeop(netsock, COMM_KEY_STAT, KEY_READY) < 0) 256 goto out; 257 if (writestr(netsock, COMM_CERT, der64) < 0) 258 goto out; 259 260 rc = 1; 261 out: 262 close(netsock); 263 if (f != NULL) 264 fclose(f); 265 free(der); 266 free(der64); 267 free(sans); 268 free(san); 269 X509_REQ_free(x); 270 X509_NAME_free(name); 271 EVP_PKEY_free(pkey); 272 ERR_print_errors_fp(stderr); 273 ERR_free_strings(); 274 return rc; 275 } 276