2 7d2a2ace 2024-01-24 op * Copyright (c) 2021, 2023, 2024 Omar Polo <op@omarpolo.com>
3 7d2a2ace 2024-01-24 op * Copyright (c) 2019 Renaud Allard <renaud@allard.it>
4 7d2a2ace 2024-01-24 op * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
5 7d2a2ace 2024-01-24 op * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6 7d2a2ace 2024-01-24 op * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org>
8 7d2a2ace 2024-01-24 op * Permission to use, copy, modify, and distribute this software for any
9 7d2a2ace 2024-01-24 op * purpose with or without fee is hereby granted, provided that the above
10 7d2a2ace 2024-01-24 op * copyright notice and this permission notice appear in all copies.
12 7d2a2ace 2024-01-24 op * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 7d2a2ace 2024-01-24 op * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 7d2a2ace 2024-01-24 op * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 7d2a2ace 2024-01-24 op * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 7d2a2ace 2024-01-24 op * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 7d2a2ace 2024-01-24 op * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 7d2a2ace 2024-01-24 op * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 d35e18b3 2024-02-04 op * The routines to generate a certificate were derived from acme-client.
25 7d2a2ace 2024-01-24 op #include "compat.h"
27 edd3a6b9 2024-02-05 op #include <sys/types.h>
28 d35e18b3 2024-02-04 op #include <sys/stat.h>
30 d35e18b3 2024-02-04 op #include <ctype.h>
31 edd3a6b9 2024-02-05 op #include <dirent.h>
32 d35e18b3 2024-02-04 op #include <fcntl.h>
33 d35e18b3 2024-02-04 op #include <limits.h>
34 7d2a2ace 2024-01-24 op #include <string.h>
35 7d2a2ace 2024-01-24 op #include <unistd.h>
37 7d2a2ace 2024-01-24 op #include <openssl/ec.h>
38 7d2a2ace 2024-01-24 op #include <openssl/err.h>
39 7d2a2ace 2024-01-24 op #include <openssl/evp.h>
40 7d2a2ace 2024-01-24 op #include <openssl/obj_mac.h>
41 7d2a2ace 2024-01-24 op #include <openssl/pem.h>
42 7d2a2ace 2024-01-24 op #include <openssl/rsa.h>
43 7d2a2ace 2024-01-24 op #include <openssl/x509_vfy.h>
44 7d2a2ace 2024-01-24 op #include <openssl/x509v3.h>
46 7d2a2ace 2024-01-24 op #include "certs.h"
47 d35e18b3 2024-02-04 op #include "fs.h"
48 d35e18b3 2024-02-04 op #include "iri.h"
50 f0e62b85 2024-02-15 op struct cstore cert_store;
52 d35e18b3 2024-02-04 op char **identities;
53 d35e18b3 2024-02-04 op static size_t id_len, id_cap;
56 7d2a2ace 2024-01-24 op * Default number of bits when creating a new RSA key.
58 7d2a2ace 2024-01-24 op #define KBITS 4096
61 d35e18b3 2024-02-04 op identities_cmp(const void *a, const void *b)
63 d35e18b3 2024-02-04 op return (strcmp(a, b));
66 d35e18b3 2024-02-04 op static inline int
67 edd3a6b9 2024-02-05 op push_identity(const char *n)
71 d35e18b3 2024-02-04 op size_t newcap, i;
73 d35e18b3 2024-02-04 op for (i = 0; i < id_len; ++i) {
74 edd3a6b9 2024-02-05 op if (!strcmp(identities[i], n))
78 2815e3a0 2024-09-01 op /* id_cap is initialized to 8 in certs_init() */
79 d35e18b3 2024-02-04 op if (id_len >= id_cap - 1) {
80 d35e18b3 2024-02-04 op newcap = id_cap + 8;
81 d35e18b3 2024-02-04 op t = recallocarray(identities, id_cap, newcap,
82 d35e18b3 2024-02-04 op sizeof(*identities));
83 d35e18b3 2024-02-04 op if (t == NULL)
85 d35e18b3 2024-02-04 op identities = t;
86 d35e18b3 2024-02-04 op id_cap = newcap;
89 edd3a6b9 2024-02-05 op if ((name = strdup(n)) == NULL)
92 edd3a6b9 2024-02-05 op identities[id_len++] = name;
97 d35e18b3 2024-02-04 op certs_cmp(const void *a, const void *b)
99 d35e18b3 2024-02-04 op const struct ccert *ca = a, *cb = b;
102 d35e18b3 2024-02-04 op if ((r = strcmp(ca->host, cb->host)) != 0)
104 d35e18b3 2024-02-04 op if ((r = strcmp(ca->port, cb->port)) != 0)
106 d35e18b3 2024-02-04 op if ((r = strcmp(ca->path, cb->path)) != 0)
108 a09bdf2b 2024-02-08 op if ((r = strcmp(ca->cert, cb->cert)) != 0)
111 a09bdf2b 2024-02-08 op if (ca->flags > cb->flags)
113 a09bdf2b 2024-02-08 op if (ca->flags < cb->flags)
119 5a39f593 2024-02-05 op certs_store_add(struct cstore *cstore, const char *host, const char *port,
120 a09bdf2b 2024-02-08 op const char *path, const char *cert, int flags)
122 5a39f593 2024-02-05 op struct ccert *c;
124 5a39f593 2024-02-05 op size_t newcap;
126 5a39f593 2024-02-05 op if (cstore->len == cstore->cap) {
127 5a39f593 2024-02-05 op newcap = cstore->cap + 8;
128 a09bdf2b 2024-02-08 op t = recallocarray(cstore->certs, cstore->cap, newcap,
129 5a39f593 2024-02-05 op sizeof(*cstore->certs));
130 5a39f593 2024-02-05 op if (t == NULL)
132 5a39f593 2024-02-05 op cstore->certs = t;
133 5a39f593 2024-02-05 op cstore->cap = newcap;
136 5a39f593 2024-02-05 op c = &cstore->certs[cstore->len];
137 a09bdf2b 2024-02-08 op c->flags = flags;
138 5a39f593 2024-02-05 op if ((c->host = strdup(host)) == NULL ||
139 5a39f593 2024-02-05 op (c->port = strdup(port)) == NULL ||
140 5a39f593 2024-02-05 op (c->path = strdup(path)) == NULL ||
141 5a39f593 2024-02-05 op (c->cert = strdup(cert)) == NULL) {
142 5a39f593 2024-02-05 op free(c->host);
143 5a39f593 2024-02-05 op free(c->port);
144 5a39f593 2024-02-05 op free(c->path);
145 5a39f593 2024-02-05 op free(c->cert);
146 5a39f593 2024-02-05 op memset(c, 0, sizeof(*c));
148 5a39f593 2024-02-05 op cstore->len++;
154 5a39f593 2024-02-05 op certs_store_parse_line(struct cstore *cstore, char *line)
156 5a39f593 2024-02-05 op char *host, *port, *path, *cert;
158 5a39f593 2024-02-05 op while (isspace((unsigned char)*line))
160 5a39f593 2024-02-05 op if (*line == '#' || *line == '\0')
165 d35e18b3 2024-02-04 op port = host + strcspn(host, " \t");
166 d35e18b3 2024-02-04 op if (*port == '\0')
168 d35e18b3 2024-02-04 op *port++ = '\0';
169 d35e18b3 2024-02-04 op while (isspace((unsigned char)*port))
172 d35e18b3 2024-02-04 op path = port + strcspn(port, " \t");
173 d35e18b3 2024-02-04 op if (*path == '\0')
175 d35e18b3 2024-02-04 op *path++ = '\0';
176 d35e18b3 2024-02-04 op while (isspace((unsigned char)*path))
179 d35e18b3 2024-02-04 op cert = path + strcspn(path, " \t");
180 d35e18b3 2024-02-04 op if (*cert == '\0')
182 d35e18b3 2024-02-04 op *cert++ = '\0';
183 d35e18b3 2024-02-04 op while (isspace((unsigned char)*cert))
186 d35e18b3 2024-02-04 op if (*cert == '\0')
189 a09bdf2b 2024-02-08 op return (certs_store_add(cstore, host, port, path, cert, CERT_OK));
193 d35e18b3 2024-02-04 op certs_init(const char *certfile)
195 edd3a6b9 2024-02-05 op struct dirent *dp;
196 edd3a6b9 2024-02-05 op DIR *certdir;
198 d35e18b3 2024-02-04 op char *line = NULL;
199 d35e18b3 2024-02-04 op size_t linesize = 0;
200 d35e18b3 2024-02-04 op ssize_t linelen;
203 d35e18b3 2024-02-04 op if ((identities = calloc(id_cap, sizeof(*identities))) == NULL)
206 edd3a6b9 2024-02-05 op if ((certdir = opendir(cert_dir)) == NULL)
209 edd3a6b9 2024-02-05 op while ((dp = readdir(certdir)) != NULL) {
210 edd3a6b9 2024-02-05 op if (dp->d_type != DT_REG)
212 edd3a6b9 2024-02-05 op if (push_identity(dp->d_name) == -1) {
213 edd3a6b9 2024-02-05 op closedir(certdir);
217 edd3a6b9 2024-02-05 op closedir(certdir);
218 edd3a6b9 2024-02-05 op qsort(identities, id_len, sizeof(*identities), identities_cmp);
220 d35e18b3 2024-02-04 op if ((fp = fopen(certfile, "r")) == NULL) {
221 d35e18b3 2024-02-04 op if (errno == ENOENT)
226 d35e18b3 2024-02-04 op while ((linelen = getline(&line, &linesize, fp)) != -1) {
227 d35e18b3 2024-02-04 op if (line[linelen - 1] == '\n')
228 d35e18b3 2024-02-04 op line[--linelen] = '\0';
230 5a39f593 2024-02-05 op if (certs_store_parse_line(&cert_store, line) == -1) {
237 d35e18b3 2024-02-04 op if (ferror(fp)) {
243 c60ec51d 2024-02-05 op qsort(cert_store.certs, cert_store.len, sizeof(*cert_store.certs),
252 d35e18b3 2024-02-04 op ccert(const char *name)
256 d35e18b3 2024-02-04 op for (i = 0; i < id_len; ++i) {
257 d35e18b3 2024-02-04 op if (!strcmp(name, identities[i]))
258 d35e18b3 2024-02-04 op return (identities[i]);
261 d35e18b3 2024-02-04 op return (NULL);
265 fc63839c 2024-02-04 op * Test whether the test path is under the certificate path.
267 fc63839c 2024-02-04 op static inline int
268 fc63839c 2024-02-04 op path_under(const char *cpath, const char *tpath)
270 fc63839c 2024-02-04 op if (*cpath == '\0')
273 fc63839c 2024-02-04 op while (*cpath != '\0') {
274 fc63839c 2024-02-04 op if (*tpath == '\0')
277 fc63839c 2024-02-04 op if (*cpath++ != *tpath++)
281 fc63839c 2024-02-04 op if (*tpath == '\0' || *tpath == '/')
284 fc63839c 2024-02-04 op return (cpath[-1] == '/');
287 5a39f593 2024-02-05 op static struct ccert *
288 4fdc9933 2024-02-05 op find_cert_for(struct cstore *cstore, struct iri *iri, size_t *n)
290 d35e18b3 2024-02-04 op struct ccert *c;
293 5a39f593 2024-02-05 op for (i = 0; i < cstore->len; ++i) {
294 5a39f593 2024-02-05 op c = &cstore->certs[i];
296 d35e18b3 2024-02-04 op if (!strcmp(c->host, iri->iri_host) &&
297 d35e18b3 2024-02-04 op !strcmp(c->port, iri->iri_portstr) &&
298 4fdc9933 2024-02-05 op path_under(c->path, iri->iri_path)) {
305 d35e18b3 2024-02-04 op return (NULL);
309 71bc1636 2024-02-06 op cert_for(struct iri *iri, int *temporary)
311 5a39f593 2024-02-05 op struct ccert *c;
313 71bc1636 2024-02-06 op *temporary = 0;
315 a09bdf2b 2024-02-08 op if ((c = find_cert_for(&cert_store, iri, NULL)) == NULL)
316 a09bdf2b 2024-02-08 op return (NULL);
317 a09bdf2b 2024-02-08 op if (c->flags & CERT_TEMP_DEL)
318 a09bdf2b 2024-02-08 op return (NULL);
320 a09bdf2b 2024-02-08 op *temporary = !!(c->flags & CERT_TEMP);
321 a09bdf2b 2024-02-08 op return (c->cert);
325 5a39f593 2024-02-05 op write_cert_file(void)
327 5a39f593 2024-02-05 op struct ccert *c;
329 5a39f593 2024-02-05 op char sfn[PATH_MAX];
333 5a39f593 2024-02-05 op strlcpy(sfn, certs_file_tmp, sizeof(sfn));
334 5a39f593 2024-02-05 op if ((fd = mkstemp(sfn)) == -1 ||
335 5a39f593 2024-02-05 op (fp = fdopen(fd, "w")) == NULL) {
336 5a39f593 2024-02-05 op if (fd != -1) {
343 5a39f593 2024-02-05 op for (i = 0; i < cert_store.len; ++i) {
344 5a39f593 2024-02-05 op c = &cert_store.certs[i];
345 a09bdf2b 2024-02-08 op if (c->flags & CERT_TEMP)
348 5a39f593 2024-02-05 op r = fprintf(fp, "%s\t%s\t%s\t%s\n", c->host, c->port,
349 5a39f593 2024-02-05 op c->path, c->cert);
357 5a39f593 2024-02-05 op if (ferror(fp)) {
363 5a39f593 2024-02-05 op if (fclose(fp) == EOF) {
368 5a39f593 2024-02-05 op if (rename(sfn, certs_file) == -1) {
377 a09bdf2b 2024-02-08 op certs_delete(struct cstore *cstore, size_t n)
379 a09bdf2b 2024-02-08 op struct ccert *c;
381 a09bdf2b 2024-02-08 op c = &cstore->certs[n];
382 a09bdf2b 2024-02-08 op free(c->host);
383 a09bdf2b 2024-02-08 op free(c->port);
384 a09bdf2b 2024-02-08 op free(c->path);
385 a09bdf2b 2024-02-08 op free(c->cert);
387 a09bdf2b 2024-02-08 op cstore->len--;
389 a09bdf2b 2024-02-08 op if (n == cstore->len) {
390 a09bdf2b 2024-02-08 op memset(&cstore->certs[n], 0, sizeof(*cstore->certs));
394 a09bdf2b 2024-02-08 op memmove(&cstore->certs[n], &cstore->certs[n + 1],
395 a09bdf2b 2024-02-08 op sizeof(*cstore->certs) * (cstore->len - n));
396 a09bdf2b 2024-02-08 op memset(&cstore->certs[cstore->len], 0, sizeof(*cstore->certs));
400 5a39f593 2024-02-05 op cert_save_for(const char *cert, struct iri *i, int persist)
402 5a39f593 2024-02-05 op struct ccert *c;
406 a09bdf2b 2024-02-08 op flags = persist ? 0 : CERT_TEMP;
408 a09bdf2b 2024-02-08 op if ((c = find_cert_for(&cert_store, i, NULL)) != NULL) {
409 5a39f593 2024-02-05 op if ((d = strdup(cert)) == NULL)
412 5a39f593 2024-02-05 op free(c->cert);
414 a09bdf2b 2024-02-08 op c->flags = flags;
416 a09bdf2b 2024-02-08 op if (certs_store_add(&cert_store, i->iri_host,
417 a09bdf2b 2024-02-08 op i->iri_portstr, i->iri_path, cert, flags) == -1)
420 a09bdf2b 2024-02-08 op qsort(cert_store.certs, cert_store.len,
421 a09bdf2b 2024-02-08 op sizeof(*cert_store.certs), certs_cmp);
424 5a39f593 2024-02-05 op if (persist && write_cert_file() == -1)
431 4fdc9933 2024-02-05 op cert_delete_for(const char *cert, struct iri *iri, int persist)
433 4fdc9933 2024-02-05 op struct ccert *c;
436 a09bdf2b 2024-02-08 op if ((c = find_cert_for(&cert_store, iri, &i)) == NULL)
439 a09bdf2b 2024-02-08 op if (!persist) {
440 a09bdf2b 2024-02-08 op c->flags |= CERT_TEMP_DEL;
444 a09bdf2b 2024-02-08 op certs_delete(&cert_store, i);
445 a09bdf2b 2024-02-08 op return (write_cert_file());
449 d35e18b3 2024-02-04 op cert_open(const char *cert)
451 d35e18b3 2024-02-04 op char path[PATH_MAX];
452 d35e18b3 2024-02-04 op struct stat sb;
455 d35e18b3 2024-02-04 op strlcpy(path, cert_dir, sizeof(path));
456 d35e18b3 2024-02-04 op strlcat(path, "/", sizeof(path));
457 d35e18b3 2024-02-04 op strlcat(path, cert, sizeof(path));
459 d35e18b3 2024-02-04 op if ((fd = open(path, O_RDONLY)) == -1)
462 d35e18b3 2024-02-04 op if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) {
470 7d2a2ace 2024-01-24 op static EVP_PKEY *
471 f0e62b85 2024-02-15 op rsa_key_create(FILE *f)
473 7d2a2ace 2024-01-24 op EVP_PKEY_CTX *ctx = NULL;
474 7d2a2ace 2024-01-24 op EVP_PKEY *pkey = NULL;
475 7d2a2ace 2024-01-24 op int ret = -1;
477 7d2a2ace 2024-01-24 op /* First, create the context and the key. */
479 7d2a2ace 2024-01-24 op if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) == NULL)
482 7d2a2ace 2024-01-24 op if (EVP_PKEY_keygen_init(ctx) <= 0)
485 7d2a2ace 2024-01-24 op if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KBITS) <= 0)
488 7d2a2ace 2024-01-24 op if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
491 7d2a2ace 2024-01-24 op /* Serialize the key to the disc. */
492 7d2a2ace 2024-01-24 op if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL))
497 7d2a2ace 2024-01-24 op if (ret == -1) {
498 7d2a2ace 2024-01-24 op EVP_PKEY_free(pkey);
501 7d2a2ace 2024-01-24 op EVP_PKEY_CTX_free(ctx);
505 7d2a2ace 2024-01-24 op static EVP_PKEY *
506 f0e62b85 2024-02-15 op ec_key_create(FILE *f)
508 7d2a2ace 2024-01-24 op EC_KEY *eckey = NULL;
509 7d2a2ace 2024-01-24 op EVP_PKEY *pkey = NULL;
510 7d2a2ace 2024-01-24 op int ret = -1;
512 7d2a2ace 2024-01-24 op if ((eckey = EC_KEY_new_by_curve_name(NID_secp384r1)) == NULL)
515 7d2a2ace 2024-01-24 op if (!EC_KEY_generate_key(eckey))
518 7d2a2ace 2024-01-24 op /* Serialise the key to the disc in EC format */
519 7d2a2ace 2024-01-24 op if (!PEM_write_ECPrivateKey(f, eckey, NULL, NULL, 0, NULL, NULL))
522 7d2a2ace 2024-01-24 op /* Convert the EC key into a PKEY structure */
523 7d2a2ace 2024-01-24 op if ((pkey = EVP_PKEY_new()) == NULL)
526 7d2a2ace 2024-01-24 op if (!EVP_PKEY_set1_EC_KEY(pkey, eckey))
531 7d2a2ace 2024-01-24 op if (ret == -1) {
532 7d2a2ace 2024-01-24 op EVP_PKEY_free(pkey);
535 7d2a2ace 2024-01-24 op EC_KEY_free(eckey);
540 f0e62b85 2024-02-15 op cert_new(const char *common_name, const char *path, int eckey)
542 7d2a2ace 2024-01-24 op EVP_PKEY *pkey = NULL;
543 7d2a2ace 2024-01-24 op X509 *x509 = NULL;
544 7d2a2ace 2024-01-24 op X509_NAME *name = NULL;
545 7d2a2ace 2024-01-24 op FILE *fp = NULL;
546 7d2a2ace 2024-01-24 op int ret = -1;
547 7d2a2ace 2024-01-24 op const unsigned char *cn = (const unsigned char*)common_name;
549 f0e62b85 2024-02-15 op if ((fp = fopen(path, "wx")) == NULL)
553 f0e62b85 2024-02-15 op pkey = ec_key_create(fp);
555 f0e62b85 2024-02-15 op pkey = rsa_key_create(fp);
556 7d2a2ace 2024-01-24 op if (pkey == NULL)
559 7d2a2ace 2024-01-24 op if ((x509 = X509_new()) == NULL)
562 7d2a2ace 2024-01-24 op ASN1_INTEGER_set(X509_get_serialNumber(x509), 0);
563 7d2a2ace 2024-01-24 op X509_gmtime_adj(X509_get_notBefore(x509), 0);
564 7d2a2ace 2024-01-24 op X509_gmtime_adj(X509_get_notAfter(x509), 315360000L); /* 10 years */
565 7d2a2ace 2024-01-24 op X509_set_version(x509, 2); // v3
567 7d2a2ace 2024-01-24 op if (!X509_set_pubkey(x509, pkey))
570 7d2a2ace 2024-01-24 op if ((name = X509_NAME_new()) == NULL)
573 7d2a2ace 2024-01-24 op if (!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, cn,
577 7d2a2ace 2024-01-24 op X509_set_subject_name(x509, name);
578 7d2a2ace 2024-01-24 op X509_set_issuer_name(x509, name);
580 7d2a2ace 2024-01-24 op if (!X509_sign(x509, pkey, EVP_sha256()))
583 7d2a2ace 2024-01-24 op if (!PEM_write_X509(fp, x509))
586 7d2a2ace 2024-01-24 op if (fflush(fp) == EOF)
592 7d2a2ace 2024-01-24 op EVP_PKEY_free(pkey);
594 7d2a2ace 2024-01-24 op X509_free(x509);
596 7d2a2ace 2024-01-24 op X509_NAME_free(name);
599 f0e62b85 2024-02-15 op if (ret == -1)
600 f0e62b85 2024-02-15 op (void) unlink(path);
601 7d2a2ace 2024-01-24 op return (ret);