1 /* $Id: certproc.c,v 1.11 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 <err.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <unistd.h> 23 24 #include <openssl/pem.h> 25 #include <openssl/x509.h> 26 #include <openssl/x509v3.h> 27 #include <openssl/err.h> 28 29 #include "extern.h" 30 31 #define MARKER "-----BEGIN CERTIFICATE-----" 32 33 /* 34 * Convert an X509 certificate to a buffer of "sz". 35 * We don't guarantee that it's NUL-terminated. 36 * Returns NULL on failure. 37 */ 38 static char * 39 x509buf(X509 *x, size_t *sz) 40 { 41 BIO *bio; 42 char *p; 43 int ssz; 44 45 /* Convert X509 to PEM in BIO. */ 46 47 if ((bio = BIO_new(BIO_s_mem())) == NULL) { 48 warnx("BIO_new"); 49 return NULL; 50 } else if (!PEM_write_bio_X509(bio, x)) { 51 warnx("PEM_write_bio_X509"); 52 BIO_free(bio); 53 return NULL; 54 } 55 56 /* 57 * Now convert bio to string. 58 * Make into NUL-terminated, just in case. 59 */ 60 61 if ((p = calloc(1, bio->num_write + 1)) == NULL) { 62 warn("calloc"); 63 BIO_free(bio); 64 return NULL; 65 } 66 67 ssz = BIO_read(bio, p, bio->num_write); 68 if (ssz < 0 || (unsigned)ssz != bio->num_write) { 69 warnx("BIO_read"); 70 BIO_free(bio); 71 return NULL; 72 } 73 74 *sz = ssz; 75 BIO_free(bio); 76 return p; 77 } 78 79 int 80 certproc(int netsock, int filesock) 81 { 82 char *csr = NULL, *chain = NULL, *url = NULL; 83 unsigned char *csrcp, *chaincp; 84 size_t csrsz, chainsz; 85 int i, rc = 0, idx = -1, cc; 86 enum certop op; 87 long lval; 88 X509 *x = NULL, *chainx = NULL; 89 X509_EXTENSION *ext = NULL; 90 X509V3_EXT_METHOD *method = NULL; 91 void *entries; 92 STACK_OF(CONF_VALUE) *val; 93 CONF_VALUE *nval; 94 95 /* File-system and sandbox jailing. */ 96 97 ERR_load_crypto_strings(); 98 99 if (pledge("stdio", NULL) == -1) { 100 warn("pledge"); 101 goto out; 102 } 103 104 /* Read what the netproc wants us to do. */ 105 106 op = CERT__MAX; 107 if ((lval = readop(netsock, COMM_CSR_OP)) == 0) 108 op = CERT_STOP; 109 else if (lval == CERT_REVOKE || lval == CERT_UPDATE) 110 op = lval; 111 112 if (CERT_STOP == op) { 113 rc = 1; 114 goto out; 115 } else if (CERT__MAX == op) { 116 warnx("unknown operation from netproc"); 117 goto out; 118 } 119 120 /* 121 * Pass revocation right through to fileproc. 122 * If the reader is terminated, ignore it. 123 */ 124 125 if (CERT_REVOKE == op) { 126 if (writeop(filesock, COMM_CHAIN_OP, FILE_REMOVE) >= 0) 127 rc = 1; 128 goto out; 129 } 130 131 /* 132 * Wait until we receive the DER encoded (signed) certificate 133 * from the network process. 134 * Then convert the DER encoding into an X509 certificate. 135 */ 136 137 if ((csr = readbuf(netsock, COMM_CSR, &csrsz)) == NULL) 138 goto out; 139 140 csrcp = (u_char *)csr; 141 x = d2i_X509(NULL, (const u_char **)&csrcp, csrsz); 142 if (x == NULL) { 143 warnx("d2i_X509"); 144 goto out; 145 } 146 147 /* 148 * Extract the CA Issuers from its NID. 149 * TODO: I have no idea what I'm doing. 150 */ 151 152 idx = X509_get_ext_by_NID(x, NID_info_access, idx); 153 if (idx >= 0 && (ext = X509_get_ext(x, idx)) != NULL) 154 method = (X509V3_EXT_METHOD *)X509V3_EXT_get(ext); 155 156 entries = X509_get_ext_d2i(x, NID_info_access, 0, 0); 157 if (method != NULL && entries != NULL) { 158 val = method->i2v(method, entries, 0); 159 for (i = 0; i < sk_CONF_VALUE_num(val); i++) { 160 nval = sk_CONF_VALUE_value(val, i); 161 if (strcmp(nval->name, "CA Issuers - URI")) 162 continue; 163 url = strdup(nval->value); 164 if (url == NULL) { 165 warn("strdup"); 166 goto out; 167 } 168 break; 169 } 170 } 171 172 if (url == NULL) { 173 warnx("no CA issuer registered with certificate"); 174 goto out; 175 } 176 177 /* Write the CA issuer to the netsock. */ 178 179 if (writestr(netsock, COMM_ISSUER, url) <= 0) 180 goto out; 181 182 /* Read the full-chain back from the netsock. */ 183 184 if ((chain = readbuf(netsock, COMM_CHAIN, &chainsz)) == NULL) 185 goto out; 186 187 /* 188 * Then check if the chain is PEM-encoded by looking to see if 189 * it begins with the PEM marker. 190 * If so, ship it as-is; otherwise, convert to a PEM encoded 191 * buffer and ship that. 192 * FIXME: if PEM, re-parse it. 193 */ 194 195 if (chainsz <= strlen(MARKER) || 196 strncmp(chain, MARKER, strlen(MARKER))) { 197 chaincp = (u_char *)chain; 198 chainx = d2i_X509(NULL, (const u_char **)&chaincp, chainsz); 199 if (chainx == NULL) { 200 warnx("d2i_X509"); 201 goto out; 202 } 203 free(chain); 204 if ((chain = x509buf(chainx, &chainsz)) == NULL) 205 goto out; 206 } 207 208 /* Allow reader termination to just push us out. */ 209 210 if ((cc = writeop(filesock, COMM_CHAIN_OP, FILE_CREATE)) == 0) 211 rc = 1; 212 if (cc <= 0) 213 goto out; 214 if ((cc = writebuf(filesock, COMM_CHAIN, chain, chainsz)) == 0) 215 rc = 1; 216 if (cc <= 0) 217 goto out; 218 219 /* 220 * Next, convert the X509 to a buffer and send that. 221 * Reader failure doesn't change anything. 222 */ 223 224 free(chain); 225 if ((chain = x509buf(x, &chainsz)) == NULL) 226 goto out; 227 if (writebuf(filesock, COMM_CSR, chain, chainsz) < 0) 228 goto out; 229 230 rc = 1; 231 out: 232 close(netsock); 233 close(filesock); 234 X509_free(x); 235 X509_free(chainx); 236 free(csr); 237 free(url); 238 free(chain); 239 ERR_print_errors_fp(stderr); 240 ERR_free_strings(); 241 return rc; 242 } 243