#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <string.h>
#include <unistd.h>
#include "log.h"
#include "ssl.h"
static char *
ssl_load_file(const char *name, off_t *len, mode_t perm)
{
struct stat st;
off_t size;
char *buf = NULL;
int fd, saved_errno;
char mode[12];
if ((fd = open(name, O_RDONLY)) == -1)
return (NULL);
if (fstat(fd, &st) != 0)
goto fail;
if (st.st_uid != 0) {
log_warnx("warn: %s: not owned by uid 0", name);
errno = EACCES;
goto fail;
}
if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) {
strmode(perm, mode);
log_warnx("warn: %s: insecure permissions: must be at most %s",
name, &mode[1]);
errno = EACCES;
goto fail;
}
size = st.st_size;
if ((buf = calloc(1, size + 1)) == NULL)
goto fail;
if (read(fd, buf, size) != size)
goto fail;
close(fd);
*len = size + 1;
return (buf);
fail:
free(buf);
saved_errno = errno;
close(fd);
errno = saved_errno;
return (NULL);
}
#if 0
static int
ssl_password_cb(char *buf, int size, int rwflag, void *u)
{
size_t len;
if (u == NULL) {
explicit_bzero(buf, size);
return (0);
}
if ((len = strlcpy(buf, u, size)) >= (size_t)size)
return (0);
return (len);
}
#endif
static int
ssl_password_cb(char *buf, int size, int rwflag, void *u)
{
int ret = 0;
size_t len;
char *pass;
pass = getpass((const char *)u);
if (pass == NULL)
return 0;
len = strlen(pass);
if (strlcpy(buf, pass, size) >= (size_t)size)
goto end;
ret = len;
end:
if (len)
explicit_bzero(pass, len);
return ret;
}
static char *
ssl_load_key(const char *name, off_t *len, char *pass, mode_t perm, const char *pkiname)
{
FILE *fp = NULL;
EVP_PKEY *key = NULL;
BIO *bio = NULL;
long size;
char *data, *buf, *filebuf;
struct stat st;
char mode[12];
char prompt[2048];
if ((fp = fopen(name, "r")) == NULL)
return (NULL);
if ((filebuf = malloc_conceal(BUFSIZ)) == NULL)
goto fail;
setvbuf(fp, filebuf, _IOFBF, BUFSIZ);
if (fstat(fileno(fp), &st) != 0)
goto fail;
if (st.st_uid != 0) {
log_warnx("warn: %s: not owned by uid 0", name);
errno = EACCES;
goto fail;
}
if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) {
strmode(perm, mode);
log_warnx("warn: %s: insecure permissions: must be at most %s",
name, &mode[1]);
errno = EACCES;
goto fail;
}
(void)snprintf(prompt, sizeof prompt, "passphrase for %s: ", pkiname);
key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, prompt);
fclose(fp);
fp = NULL;
freezero(filebuf, BUFSIZ);
filebuf = NULL;
if (key == NULL)
goto fail;
if ((bio = BIO_new(BIO_s_mem())) == NULL)
goto fail;
if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
goto fail;
if ((size = BIO_get_mem_data(bio, &data)) <= 0)
goto fail;
if ((buf = calloc_conceal(1, size + 1)) == NULL)
goto fail;
memcpy(buf, data, size);
BIO_free_all(bio);
EVP_PKEY_free(key);
*len = (off_t)size + 1;
return (buf);
fail:
ssl_error("ssl_load_key");
BIO_free_all(bio);
EVP_PKEY_free(key);
if (fp)
fclose(fp);
freezero(filebuf, BUFSIZ);
return (NULL);
}
int
ssl_load_certificate(struct pki *p, const char *pathname)
{
p->pki_cert = ssl_load_file(pathname, &p->pki_cert_len, 0755);
if (p->pki_cert == NULL)
return 0;
return 1;
}
int
ssl_load_keyfile(struct pki *p, const char *pathname, const char *pkiname)
{
char pass[1024];
p->pki_key = ssl_load_key(pathname, &p->pki_key_len, pass, 0740, pkiname);
if (p->pki_key == NULL)
return 0;
return 1;
}
int
ssl_load_cafile(struct ca *c, const char *pathname)
{
c->ca_cert = ssl_load_file(pathname, &c->ca_cert_len, 0755);
if (c->ca_cert == NULL)
return 0;
return 1;
}
void
ssl_error(const char *where)
{
unsigned long code;
char errbuf[128];
for (; (code = ERR_get_error()) != 0 ;) {
ERR_error_string_n(code, errbuf, sizeof(errbuf));
log_debug("debug: SSL library error: %s: %s", where, errbuf);
}
}
static void
hash_x509(X509 *cert, char *hash, size_t hashlen)
{
static const char hex[] = "0123456789abcdef";
size_t off;
char digest[EVP_MAX_MD_SIZE];
int dlen, i;
if (X509_pubkey_digest(cert, EVP_sha256(), digest, &dlen) != 1)
fatalx("%s: X509_pubkey_digest failed", __func__);
if (hashlen < 2 * dlen + sizeof("SHA256:"))
fatalx("%s: hash buffer too small", __func__);
off = strlcpy(hash, "SHA256:", hashlen);
for (i = 0; i < dlen; i++) {
hash[off++] = hex[(digest[i] >> 4) & 0x0f];
hash[off++] = hex[digest[i] & 0x0f];
}
hash[off] = 0;
}
char *
ssl_pubkey_hash(const char *buf, off_t len)
{
#define TLS_CERT_HASH_SIZE 128
BIO *in;
X509 *x509 = NULL;
char *hash = NULL;
if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
log_warnx("%s: BIO_new_mem_buf failed", __func__);
return NULL;
}
if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) {
log_warnx("%s: PEM_read_bio_X509 failed", __func__);
goto fail;
}
if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) {
log_warn("%s: malloc", __func__);
goto fail;
}
hash_x509(x509, hash, TLS_CERT_HASH_SIZE);
fail:
BIO_free(in);
if (x509)
X509_free(x509);
return hash;
}