1 /* $OpenBSD: ssl.c,v 1.100 2023/06/25 08:08:03 op Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 5 * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org> 6 * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/stat.h> 22 23 #include <fcntl.h> 24 #include <limits.h> 25 #include <openssl/err.h> 26 #include <openssl/ssl.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "log.h" 31 #include "ssl.h" 32 33 static char * 34 ssl_load_file(const char *name, off_t *len, mode_t perm) 35 { 36 struct stat st; 37 off_t size; 38 char *buf = NULL; 39 int fd, saved_errno; 40 char mode[12]; 41 42 if ((fd = open(name, O_RDONLY)) == -1) 43 return (NULL); 44 if (fstat(fd, &st) != 0) 45 goto fail; 46 if (st.st_uid != 0) { 47 log_warnx("warn: %s: not owned by uid 0", name); 48 errno = EACCES; 49 goto fail; 50 } 51 if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) { 52 strmode(perm, mode); 53 log_warnx("warn: %s: insecure permissions: must be at most %s", 54 name, &mode[1]); 55 errno = EACCES; 56 goto fail; 57 } 58 size = st.st_size; 59 if ((buf = calloc(1, size + 1)) == NULL) 60 goto fail; 61 if (read(fd, buf, size) != size) 62 goto fail; 63 close(fd); 64 65 *len = size + 1; 66 return (buf); 67 68 fail: 69 free(buf); 70 saved_errno = errno; 71 close(fd); 72 errno = saved_errno; 73 return (NULL); 74 } 75 76 #if 0 77 static int 78 ssl_password_cb(char *buf, int size, int rwflag, void *u) 79 { 80 size_t len; 81 if (u == NULL) { 82 explicit_bzero(buf, size); 83 return (0); 84 } 85 if ((len = strlcpy(buf, u, size)) >= (size_t)size) 86 return (0); 87 return (len); 88 } 89 #endif 90 91 static int 92 ssl_password_cb(char *buf, int size, int rwflag, void *u) 93 { 94 int ret = 0; 95 size_t len; 96 char *pass; 97 98 pass = getpass((const char *)u); 99 if (pass == NULL) 100 return 0; 101 len = strlen(pass); 102 if (strlcpy(buf, pass, size) >= (size_t)size) 103 goto end; 104 ret = len; 105 end: 106 if (len) 107 explicit_bzero(pass, len); 108 return ret; 109 } 110 111 static char * 112 ssl_load_key(const char *name, off_t *len, char *pass, mode_t perm, const char *pkiname) 113 { 114 FILE *fp = NULL; 115 EVP_PKEY *key = NULL; 116 BIO *bio = NULL; 117 long size; 118 char *data, *buf, *filebuf; 119 struct stat st; 120 char mode[12]; 121 char prompt[2048]; 122 123 /* 124 * Read (possibly) encrypted key from file 125 */ 126 if ((fp = fopen(name, "r")) == NULL) 127 return (NULL); 128 if ((filebuf = malloc_conceal(BUFSIZ)) == NULL) 129 goto fail; 130 setvbuf(fp, filebuf, _IOFBF, BUFSIZ); 131 132 if (fstat(fileno(fp), &st) != 0) 133 goto fail; 134 if (st.st_uid != 0) { 135 log_warnx("warn: %s: not owned by uid 0", name); 136 errno = EACCES; 137 goto fail; 138 } 139 if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) { 140 strmode(perm, mode); 141 log_warnx("warn: %s: insecure permissions: must be at most %s", 142 name, &mode[1]); 143 errno = EACCES; 144 goto fail; 145 } 146 147 (void)snprintf(prompt, sizeof prompt, "passphrase for %s: ", pkiname); 148 key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, prompt); 149 fclose(fp); 150 fp = NULL; 151 freezero(filebuf, BUFSIZ); 152 filebuf = NULL; 153 if (key == NULL) 154 goto fail; 155 /* 156 * Write unencrypted key to memory buffer 157 */ 158 if ((bio = BIO_new(BIO_s_mem())) == NULL) 159 goto fail; 160 if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) 161 goto fail; 162 if ((size = BIO_get_mem_data(bio, &data)) <= 0) 163 goto fail; 164 if ((buf = calloc_conceal(1, size + 1)) == NULL) 165 goto fail; 166 memcpy(buf, data, size); 167 168 BIO_free_all(bio); 169 EVP_PKEY_free(key); 170 171 *len = (off_t)size + 1; 172 return (buf); 173 174 fail: 175 ssl_error("ssl_load_key"); 176 BIO_free_all(bio); 177 EVP_PKEY_free(key); 178 if (fp) 179 fclose(fp); 180 freezero(filebuf, BUFSIZ); 181 return (NULL); 182 } 183 184 int 185 ssl_load_certificate(struct pki *p, const char *pathname) 186 { 187 p->pki_cert = ssl_load_file(pathname, &p->pki_cert_len, 0755); 188 if (p->pki_cert == NULL) 189 return 0; 190 return 1; 191 } 192 193 int 194 ssl_load_keyfile(struct pki *p, const char *pathname, const char *pkiname) 195 { 196 char pass[1024]; 197 198 p->pki_key = ssl_load_key(pathname, &p->pki_key_len, pass, 0740, pkiname); 199 if (p->pki_key == NULL) 200 return 0; 201 return 1; 202 } 203 204 int 205 ssl_load_cafile(struct ca *c, const char *pathname) 206 { 207 c->ca_cert = ssl_load_file(pathname, &c->ca_cert_len, 0755); 208 if (c->ca_cert == NULL) 209 return 0; 210 return 1; 211 } 212 213 void 214 ssl_error(const char *where) 215 { 216 unsigned long code; 217 char errbuf[128]; 218 219 for (; (code = ERR_get_error()) != 0 ;) { 220 ERR_error_string_n(code, errbuf, sizeof(errbuf)); 221 log_debug("debug: SSL library error: %s: %s", where, errbuf); 222 } 223 } 224 225 static void 226 hash_x509(X509 *cert, char *hash, size_t hashlen) 227 { 228 static const char hex[] = "0123456789abcdef"; 229 size_t off; 230 char digest[EVP_MAX_MD_SIZE]; 231 int dlen, i; 232 233 if (X509_pubkey_digest(cert, EVP_sha256(), digest, &dlen) != 1) 234 fatalx("%s: X509_pubkey_digest failed", __func__); 235 236 if (hashlen < 2 * dlen + sizeof("SHA256:")) 237 fatalx("%s: hash buffer too small", __func__); 238 239 off = strlcpy(hash, "SHA256:", hashlen); 240 241 for (i = 0; i < dlen; i++) { 242 hash[off++] = hex[(digest[i] >> 4) & 0x0f]; 243 hash[off++] = hex[digest[i] & 0x0f]; 244 } 245 hash[off] = 0; 246 } 247 248 char * 249 ssl_pubkey_hash(const char *buf, off_t len) 250 { 251 #define TLS_CERT_HASH_SIZE 128 252 BIO *in; 253 X509 *x509 = NULL; 254 char *hash = NULL; 255 256 if ((in = BIO_new_mem_buf(buf, len)) == NULL) { 257 log_warnx("%s: BIO_new_mem_buf failed", __func__); 258 return NULL; 259 } 260 261 if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) { 262 log_warnx("%s: PEM_read_bio_X509 failed", __func__); 263 goto fail; 264 } 265 266 if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) { 267 log_warn("%s: malloc", __func__); 268 goto fail; 269 } 270 hash_x509(x509, hash, TLS_CERT_HASH_SIZE); 271 272 fail: 273 BIO_free(in); 274 275 if (x509) 276 X509_free(x509); 277 278 return hash; 279 } 280