1*97028011Sop /* $OpenBSD: ssl.c,v 1.100 2023/06/25 08:08:03 op Exp $ */
21f3c2bcfSsobrado
33ef9cbf7Sgilles /*
43ef9cbf7Sgilles * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
53ef9cbf7Sgilles * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org>
665c4fdfbSgilles * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
73ef9cbf7Sgilles *
83ef9cbf7Sgilles * Permission to use, copy, modify, and distribute this software for any
93ef9cbf7Sgilles * purpose with or without fee is hereby granted, provided that the above
103ef9cbf7Sgilles * copyright notice and this permission notice appear in all copies.
113ef9cbf7Sgilles *
123ef9cbf7Sgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
133ef9cbf7Sgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
143ef9cbf7Sgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
153ef9cbf7Sgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
163ef9cbf7Sgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
173ef9cbf7Sgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
183ef9cbf7Sgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
193ef9cbf7Sgilles */
203ef9cbf7Sgilles
213ef9cbf7Sgilles #include <sys/stat.h>
223ef9cbf7Sgilles
233ef9cbf7Sgilles #include <fcntl.h>
24299c4efeSeric #include <limits.h>
2541b8cf0bSmillert #include <openssl/err.h>
26d3140113Seric #include <openssl/ssl.h>
27d3140113Seric #include <string.h>
28d3140113Seric #include <unistd.h>
293ef9cbf7Sgilles
305eb8dddaSgilles #include "log.h"
3165c4fdfbSgilles #include "ssl.h"
323ef9cbf7Sgilles
334889cc2aSeric static char *
ssl_load_file(const char * name,off_t * len,mode_t perm)34bbdba859Shalex ssl_load_file(const char *name, off_t *len, mode_t perm)
353ef9cbf7Sgilles {
363ef9cbf7Sgilles struct stat st;
373ef9cbf7Sgilles off_t size;
383ef9cbf7Sgilles char *buf = NULL;
393677b057Seric int fd, saved_errno;
40bbdba859Shalex char mode[12];
413ef9cbf7Sgilles
423ef9cbf7Sgilles if ((fd = open(name, O_RDONLY)) == -1)
433ef9cbf7Sgilles return (NULL);
443ef9cbf7Sgilles if (fstat(fd, &st) != 0)
453ef9cbf7Sgilles goto fail;
463677b057Seric if (st.st_uid != 0) {
4782614934Seric log_warnx("warn: %s: not owned by uid 0", name);
483677b057Seric errno = EACCES;
493677b057Seric goto fail;
503677b057Seric }
51bbdba859Shalex if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) {
52bbdba859Shalex strmode(perm, mode);
5382614934Seric log_warnx("warn: %s: insecure permissions: must be at most %s",
54bbdba859Shalex name, &mode[1]);
553677b057Seric errno = EACCES;
563677b057Seric goto fail;
573677b057Seric }
583ef9cbf7Sgilles size = st.st_size;
593ef9cbf7Sgilles if ((buf = calloc(1, size + 1)) == NULL)
603ef9cbf7Sgilles goto fail;
613ef9cbf7Sgilles if (read(fd, buf, size) != size)
623ef9cbf7Sgilles goto fail;
633ef9cbf7Sgilles close(fd);
643ef9cbf7Sgilles
653ef9cbf7Sgilles *len = size + 1;
663ef9cbf7Sgilles return (buf);
673ef9cbf7Sgilles
683ef9cbf7Sgilles fail:
693ef9cbf7Sgilles free(buf);
703677b057Seric saved_errno = errno;
713ef9cbf7Sgilles close(fd);
723677b057Seric errno = saved_errno;
733ef9cbf7Sgilles return (NULL);
743ef9cbf7Sgilles }
753ef9cbf7Sgilles
7676a0ecd1Seric #if 0
7765c4fdfbSgilles static int
7865c4fdfbSgilles ssl_password_cb(char *buf, int size, int rwflag, void *u)
7965c4fdfbSgilles {
8065c4fdfbSgilles size_t len;
8165c4fdfbSgilles if (u == NULL) {
828fbd7fcbSdoug explicit_bzero(buf, size);
8365c4fdfbSgilles return (0);
8465c4fdfbSgilles }
8565c4fdfbSgilles if ((len = strlcpy(buf, u, size)) >= (size_t)size)
8665c4fdfbSgilles return (0);
8765c4fdfbSgilles return (len);
8865c4fdfbSgilles }
8976a0ecd1Seric #endif
9076a0ecd1Seric
9176a0ecd1Seric static int
ssl_password_cb(char * buf,int size,int rwflag,void * u)92d5cc82f9Sreyk ssl_password_cb(char *buf, int size, int rwflag, void *u)
9376a0ecd1Seric {
9476a0ecd1Seric int ret = 0;
9576a0ecd1Seric size_t len;
9676a0ecd1Seric char *pass;
9776a0ecd1Seric
9876a0ecd1Seric pass = getpass((const char *)u);
9976a0ecd1Seric if (pass == NULL)
10076a0ecd1Seric return 0;
10176a0ecd1Seric len = strlen(pass);
10276a0ecd1Seric if (strlcpy(buf, pass, size) >= (size_t)size)
10376a0ecd1Seric goto end;
10476a0ecd1Seric ret = len;
10576a0ecd1Seric end:
10676a0ecd1Seric if (len)
1078fbd7fcbSdoug explicit_bzero(pass, len);
10876a0ecd1Seric return ret;
10976a0ecd1Seric }
11065c4fdfbSgilles
1114889cc2aSeric static char *
ssl_load_key(const char * name,off_t * len,char * pass,mode_t perm,const char * pkiname)11276a0ecd1Seric ssl_load_key(const char *name, off_t *len, char *pass, mode_t perm, const char *pkiname)
11365c4fdfbSgilles {
1141c3ac238Seric FILE *fp = NULL;
11565c4fdfbSgilles EVP_PKEY *key = NULL;
11665c4fdfbSgilles BIO *bio = NULL;
11765c4fdfbSgilles long size;
118cdaf00a5Sotto char *data, *buf, *filebuf;
11976a0ecd1Seric struct stat st;
12076a0ecd1Seric char mode[12];
12176a0ecd1Seric char prompt[2048];
12265c4fdfbSgilles
12365c4fdfbSgilles /*
12465c4fdfbSgilles * Read (possibly) encrypted key from file
12565c4fdfbSgilles */
12665c4fdfbSgilles if ((fp = fopen(name, "r")) == NULL)
12765c4fdfbSgilles return (NULL);
128cdaf00a5Sotto if ((filebuf = malloc_conceal(BUFSIZ)) == NULL)
129cdaf00a5Sotto goto fail;
130cdaf00a5Sotto setvbuf(fp, filebuf, _IOFBF, BUFSIZ);
13165c4fdfbSgilles
13276a0ecd1Seric if (fstat(fileno(fp), &st) != 0)
13376a0ecd1Seric goto fail;
13476a0ecd1Seric if (st.st_uid != 0) {
13576a0ecd1Seric log_warnx("warn: %s: not owned by uid 0", name);
13676a0ecd1Seric errno = EACCES;
13776a0ecd1Seric goto fail;
13876a0ecd1Seric }
13976a0ecd1Seric if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) {
14076a0ecd1Seric strmode(perm, mode);
14176a0ecd1Seric log_warnx("warn: %s: insecure permissions: must be at most %s",
14276a0ecd1Seric name, &mode[1]);
14376a0ecd1Seric errno = EACCES;
14476a0ecd1Seric goto fail;
14576a0ecd1Seric }
14676a0ecd1Seric
14776a0ecd1Seric (void)snprintf(prompt, sizeof prompt, "passphrase for %s: ", pkiname);
148d5cc82f9Sreyk key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, prompt);
14965c4fdfbSgilles fclose(fp);
1501c3ac238Seric fp = NULL;
151cdaf00a5Sotto freezero(filebuf, BUFSIZ);
152cdaf00a5Sotto filebuf = NULL;
15365c4fdfbSgilles if (key == NULL)
15465c4fdfbSgilles goto fail;
15565c4fdfbSgilles /*
15665c4fdfbSgilles * Write unencrypted key to memory buffer
15765c4fdfbSgilles */
15865c4fdfbSgilles if ((bio = BIO_new(BIO_s_mem())) == NULL)
15965c4fdfbSgilles goto fail;
16065c4fdfbSgilles if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
16165c4fdfbSgilles goto fail;
16265c4fdfbSgilles if ((size = BIO_get_mem_data(bio, &data)) <= 0)
16365c4fdfbSgilles goto fail;
164cdaf00a5Sotto if ((buf = calloc_conceal(1, size + 1)) == NULL)
16565c4fdfbSgilles goto fail;
16665c4fdfbSgilles memcpy(buf, data, size);
16765c4fdfbSgilles
16865c4fdfbSgilles BIO_free_all(bio);
1695a039c14Sreyk EVP_PKEY_free(key);
1705a039c14Sreyk
17176a0ecd1Seric *len = (off_t)size + 1;
17265c4fdfbSgilles return (buf);
17365c4fdfbSgilles
17465c4fdfbSgilles fail:
17565c4fdfbSgilles ssl_error("ssl_load_key");
17665c4fdfbSgilles BIO_free_all(bio);
1775a039c14Sreyk EVP_PKEY_free(key);
17867aa17dcSeric if (fp)
1791c3ac238Seric fclose(fp);
180cdaf00a5Sotto freezero(filebuf, BUFSIZ);
18165c4fdfbSgilles return (NULL);
18265c4fdfbSgilles }
18365c4fdfbSgilles
1843ef9cbf7Sgilles int
ssl_load_certificate(struct pki * p,const char * pathname)1851c3ac238Seric ssl_load_certificate(struct pki *p, const char *pathname)
1863ef9cbf7Sgilles {
1871c3ac238Seric p->pki_cert = ssl_load_file(pathname, &p->pki_cert_len, 0755);
1881c3ac238Seric if (p->pki_cert == NULL)
189cc81b7c6Seric return 0;
190cc81b7c6Seric return 1;
19155dc621fSgilles }
19255dc621fSgilles
193cc81b7c6Seric int
ssl_load_keyfile(struct pki * p,const char * pathname,const char * pkiname)1941c3ac238Seric ssl_load_keyfile(struct pki *p, const char *pathname, const char *pkiname)
195cc81b7c6Seric {
19676a0ecd1Seric char pass[1024];
19776a0ecd1Seric
1985f122c69Sgilles p->pki_key = ssl_load_key(pathname, &p->pki_key_len, pass, 0740, pkiname);
1991c3ac238Seric if (p->pki_key == NULL)
200cc81b7c6Seric return 0;
201cc81b7c6Seric return 1;
202cc81b7c6Seric }
203cc81b7c6Seric
204cc81b7c6Seric int
ssl_load_cafile(struct ca * c,const char * pathname)205f7aa1c30Sgilles ssl_load_cafile(struct ca *c, const char *pathname)
206cc81b7c6Seric {
207f7aa1c30Sgilles c->ca_cert = ssl_load_file(pathname, &c->ca_cert_len, 0755);
208f7aa1c30Sgilles if (c->ca_cert == NULL)
209cc81b7c6Seric return 0;
210cc81b7c6Seric return 1;
211cc81b7c6Seric }
212cc81b7c6Seric
2133ef9cbf7Sgilles void
ssl_error(const char * where)2143ef9cbf7Sgilles ssl_error(const char *where)
2153ef9cbf7Sgilles {
2163ef9cbf7Sgilles unsigned long code;
2173ef9cbf7Sgilles char errbuf[128];
2183ef9cbf7Sgilles
2193ef9cbf7Sgilles for (; (code = ERR_get_error()) != 0 ;) {
2203ef9cbf7Sgilles ERR_error_string_n(code, errbuf, sizeof(errbuf));
22182614934Seric log_debug("debug: SSL library error: %s: %s", where, errbuf);
2223ef9cbf7Sgilles }
2233ef9cbf7Sgilles }
22441b8cf0bSmillert
22541b8cf0bSmillert static void
hash_x509(X509 * cert,char * hash,size_t hashlen)22641b8cf0bSmillert hash_x509(X509 *cert, char *hash, size_t hashlen)
22741b8cf0bSmillert {
22841b8cf0bSmillert static const char hex[] = "0123456789abcdef";
22941b8cf0bSmillert size_t off;
23041b8cf0bSmillert char digest[EVP_MAX_MD_SIZE];
23141b8cf0bSmillert int dlen, i;
23241b8cf0bSmillert
23341b8cf0bSmillert if (X509_pubkey_digest(cert, EVP_sha256(), digest, &dlen) != 1)
23441b8cf0bSmillert fatalx("%s: X509_pubkey_digest failed", __func__);
23541b8cf0bSmillert
23641b8cf0bSmillert if (hashlen < 2 * dlen + sizeof("SHA256:"))
2377078bf6aSop fatalx("%s: hash buffer too small", __func__);
23841b8cf0bSmillert
23941b8cf0bSmillert off = strlcpy(hash, "SHA256:", hashlen);
24041b8cf0bSmillert
24141b8cf0bSmillert for (i = 0; i < dlen; i++) {
24241b8cf0bSmillert hash[off++] = hex[(digest[i] >> 4) & 0x0f];
24341b8cf0bSmillert hash[off++] = hex[digest[i] & 0x0f];
24441b8cf0bSmillert }
24541b8cf0bSmillert hash[off] = 0;
24641b8cf0bSmillert }
24741b8cf0bSmillert
24841b8cf0bSmillert char *
ssl_pubkey_hash(const char * buf,off_t len)24941b8cf0bSmillert ssl_pubkey_hash(const char *buf, off_t len)
25041b8cf0bSmillert {
25141b8cf0bSmillert #define TLS_CERT_HASH_SIZE 128
25241b8cf0bSmillert BIO *in;
25341b8cf0bSmillert X509 *x509 = NULL;
25441b8cf0bSmillert char *hash = NULL;
25541b8cf0bSmillert
25641b8cf0bSmillert if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
25741b8cf0bSmillert log_warnx("%s: BIO_new_mem_buf failed", __func__);
25841b8cf0bSmillert return NULL;
25941b8cf0bSmillert }
26041b8cf0bSmillert
26141b8cf0bSmillert if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) {
26241b8cf0bSmillert log_warnx("%s: PEM_read_bio_X509 failed", __func__);
26341b8cf0bSmillert goto fail;
26441b8cf0bSmillert }
26541b8cf0bSmillert
26641b8cf0bSmillert if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) {
26741b8cf0bSmillert log_warn("%s: malloc", __func__);
26841b8cf0bSmillert goto fail;
26941b8cf0bSmillert }
27041b8cf0bSmillert hash_x509(x509, hash, TLS_CERT_HASH_SIZE);
27141b8cf0bSmillert
27241b8cf0bSmillert fail:
27341b8cf0bSmillert BIO_free(in);
27441b8cf0bSmillert
27541b8cf0bSmillert if (x509)
27641b8cf0bSmillert X509_free(x509);
27741b8cf0bSmillert
27841b8cf0bSmillert return hash;
27941b8cf0bSmillert }
280