Blob


1 /*
2 * Copyright (c) 2021, 2023, 2024 Omar Polo <op@omarpolo.com>
3 * Copyright (c) 2019 Renaud Allard <renaud@allard.it>
4 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
5 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6 * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.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 */
21 /*
22 * The routines to generate a certificate were derived from acme-client.
23 */
25 #include "compat.h"
27 #include <sys/types.h>
28 #include <sys/stat.h>
30 #include <ctype.h>
31 #include <dirent.h>
32 #include <fcntl.h>
33 #include <limits.h>
34 #include <string.h>
35 #include <unistd.h>
37 #include <openssl/ec.h>
38 #include <openssl/err.h>
39 #include <openssl/evp.h>
40 #include <openssl/obj_mac.h>
41 #include <openssl/pem.h>
42 #include <openssl/rsa.h>
43 #include <openssl/x509_vfy.h>
44 #include <openssl/x509v3.h>
46 #include "certs.h"
47 #include "fs.h"
48 #include "iri.h"
50 /* client certificate */
51 struct ccert {
52 char *host;
53 char *port;
54 char *path;
55 char *cert;
57 #define CERT_OK 0x00
58 #define CERT_TEMP 0x01
59 #define CERT_TEMP_DEL 0x02
60 int flags;
61 };
63 static struct cstore {
64 struct ccert *certs;
65 size_t len;
66 size_t cap;
67 } cert_store;
69 char **identities;
70 static size_t id_len, id_cap;
72 /*
73 * Default number of bits when creating a new RSA key.
74 */
75 #define KBITS 4096
77 static int
78 identities_cmp(const void *a, const void *b)
79 {
80 return (strcmp(a, b));
81 }
83 static inline int
84 push_identity(const char *n)
85 {
86 char *name;
87 void *t;
88 size_t newcap, i;
90 for (i = 0; i < id_len; ++i) {
91 if (!strcmp(identities[i], n))
92 return (0);
93 }
95 /* id_cap is initilized to 8 in certs_init() */
96 if (id_len >= id_cap - 1) {
97 newcap = id_cap + 8;
98 t = recallocarray(identities, id_cap, newcap,
99 sizeof(*identities));
100 if (t == NULL)
101 return (-1);
102 identities = t;
103 id_cap = newcap;
106 if ((name = strdup(n)) == NULL)
107 return (-1);
109 identities[id_len++] = name;
110 return (0);
113 static int
114 certs_cmp(const void *a, const void *b)
116 const struct ccert *ca = a, *cb = b;
117 int r;
119 if ((r = strcmp(ca->host, cb->host)) != 0)
120 return (r);
121 if ((r = strcmp(ca->port, cb->port)) != 0)
122 return (r);
123 if ((r = strcmp(ca->path, cb->path)) != 0)
124 return (r);
125 if ((r = strcmp(ca->cert, cb->cert)) != 0)
126 return (r);
128 if (ca->flags > cb->flags)
129 return (+1);
130 if (ca->flags < cb->flags)
131 return (-1);
132 return (0);
135 static int
136 certs_store_add(struct cstore *cstore, const char *host, const char *port,
137 const char *path, const char *cert, int flags)
139 struct ccert *c;
140 void *t;
141 size_t newcap;
143 if (cstore->len == cstore->cap) {
144 newcap = cstore->cap + 8;
145 t = recallocarray(cstore->certs, cstore->cap, newcap,
146 sizeof(*cstore->certs));
147 if (t == NULL)
148 return (-1);
149 cstore->certs = t;
150 cstore->cap = newcap;
153 c = &cstore->certs[cstore->len];
154 c->flags = flags;
155 if ((c->host = strdup(host)) == NULL ||
156 (c->port = strdup(port)) == NULL ||
157 (c->path = strdup(path)) == NULL ||
158 (c->cert = strdup(cert)) == NULL) {
159 free(c->host);
160 free(c->port);
161 free(c->path);
162 free(c->cert);
163 memset(c, 0, sizeof(*c));
165 cstore->len++;
167 return (0);
170 static int
171 certs_store_parse_line(struct cstore *cstore, char *line)
173 char *host, *port, *path, *cert;
175 while (isspace((unsigned char)*line))
176 ++line;
177 if (*line == '#' || *line == '\0')
178 return (0);
180 host = line;
182 port = host + strcspn(host, " \t");
183 if (*port == '\0')
184 return (-1);
185 *port++ = '\0';
186 while (isspace((unsigned char)*port))
187 ++port;
189 path = port + strcspn(port, " \t");
190 if (*path == '\0')
191 return (-1);
192 *path++ = '\0';
193 while (isspace((unsigned char)*path))
194 ++path;
196 cert = path + strcspn(path, " \t");
197 if (*cert == '\0')
198 return (-1);
199 *cert++ = '\0';
200 while (isspace((unsigned char)*cert))
201 ++cert;
203 if (*cert == '\0')
204 return (-1);
206 return (certs_store_add(cstore, host, port, path, cert, CERT_OK));
209 int
210 certs_init(const char *certfile)
212 struct dirent *dp;
213 DIR *certdir;
214 FILE *fp;
215 char *line = NULL;
216 size_t linesize = 0;
217 ssize_t linelen;
219 id_cap = 8;
220 if ((identities = calloc(id_cap, sizeof(*identities))) == NULL)
221 return (-1);
223 if ((certdir = opendir(cert_dir)) == NULL)
224 return (-1);
226 while ((dp = readdir(certdir)) != NULL) {
227 if (dp->d_type != DT_REG)
228 continue;
229 if (push_identity(dp->d_name) == -1) {
230 closedir(certdir);
231 return (-1);
234 closedir(certdir);
235 qsort(identities, id_len, sizeof(*identities), identities_cmp);
237 if ((fp = fopen(certfile, "r")) == NULL) {
238 if (errno == ENOENT)
239 return (0);
240 return (-1);
243 while ((linelen = getline(&line, &linesize, fp)) != -1) {
244 if (line[linelen - 1] == '\n')
245 line[--linelen] = '\0';
247 if (certs_store_parse_line(&cert_store, line) == -1) {
248 fclose(fp);
249 free(line);
250 return (-1);
254 if (ferror(fp)) {
255 fclose(fp);
256 free(line);
257 return (-1);
260 qsort(cert_store.certs, cert_store.len, sizeof(*cert_store.certs),
261 certs_cmp);
263 fclose(fp);
264 free(line);
265 return (0);
268 const char *
269 ccert(const char *name)
271 size_t i;
273 for (i = 0; i < id_len; ++i) {
274 if (!strcmp(name, identities[i]))
275 return (identities[i]);
278 return (NULL);
281 /*
282 * Test whether the test path is under the certificate path.
283 */
284 static inline int
285 path_under(const char *cpath, const char *tpath)
287 if (*cpath == '\0')
288 return (1);
290 while (*cpath != '\0') {
291 if (*tpath == '\0')
292 return (0);
294 if (*cpath++ != *tpath++)
295 return (0);
298 if (*tpath == '\0' || *tpath == '/')
299 return (1);
301 return (cpath[-1] == '/');
304 static struct ccert *
305 find_cert_for(struct cstore *cstore, struct iri *iri, size_t *n)
307 struct ccert *c;
308 size_t i;
310 for (i = 0; i < cstore->len; ++i) {
311 c = &cstore->certs[i];
313 if (!strcmp(c->host, iri->iri_host) &&
314 !strcmp(c->port, iri->iri_portstr) &&
315 path_under(c->path, iri->iri_path)) {
316 if (n)
317 *n = i;
318 return (c);
322 return (NULL);
325 const char *
326 cert_for(struct iri *iri, int *temporary)
328 struct ccert *c;
330 *temporary = 0;
332 if ((c = find_cert_for(&cert_store, iri, NULL)) == NULL)
333 return (NULL);
334 if (c->flags & CERT_TEMP_DEL)
335 return (NULL);
337 *temporary = !!(c->flags & CERT_TEMP);
338 return (c->cert);
341 static int
342 write_cert_file(void)
344 struct ccert *c;
345 FILE *fp;
346 char sfn[PATH_MAX];
347 size_t i;
348 int fd, r;
350 strlcpy(sfn, certs_file_tmp, sizeof(sfn));
351 if ((fd = mkstemp(sfn)) == -1 ||
352 (fp = fdopen(fd, "w")) == NULL) {
353 if (fd != -1) {
354 unlink(sfn);
355 close(fd);
357 return (-1);
360 for (i = 0; i < cert_store.len; ++i) {
361 c = &cert_store.certs[i];
362 if (c->flags & CERT_TEMP)
363 continue;
365 r = fprintf(fp, "%s\t%s\t%s\t%s\n", c->host, c->port,
366 c->path, c->cert);
367 if (r < 0) {
368 fclose(fp);
369 unlink(sfn);
370 return (-1);
374 if (ferror(fp)) {
375 fclose(fp);
376 unlink(sfn);
377 return (-1);
380 if (fclose(fp) == EOF) {
381 unlink(sfn);
382 return (-1);
385 if (rename(sfn, certs_file) == -1) {
386 unlink(sfn);
387 return (-1);
390 return (0);
393 static void
394 certs_delete(struct cstore *cstore, size_t n)
396 struct ccert *c;
398 c = &cstore->certs[n];
399 free(c->host);
400 free(c->port);
401 free(c->path);
402 free(c->cert);
404 cstore->len--;
406 if (n == cstore->len) {
407 memset(&cstore->certs[n], 0, sizeof(*cstore->certs));
408 return;
411 memmove(&cstore->certs[n], &cstore->certs[n + 1],
412 sizeof(*cstore->certs) * (cstore->len - n));
413 memset(&cstore->certs[cstore->len], 0, sizeof(*cstore->certs));
416 int
417 cert_save_for(const char *cert, struct iri *i, int persist)
419 struct ccert *c;
420 char *d;
421 int flags;
423 flags = persist ? 0 : CERT_TEMP;
425 if ((c = find_cert_for(&cert_store, i, NULL)) != NULL) {
426 if ((d = strdup(cert)) == NULL)
427 return (-1);
429 free(c->cert);
430 c->cert = d;
431 c->flags = flags;
432 } else {
433 if (certs_store_add(&cert_store, i->iri_host,
434 i->iri_portstr, i->iri_path, cert, flags) == -1)
435 return (-1);
437 qsort(cert_store.certs, cert_store.len,
438 sizeof(*cert_store.certs), certs_cmp);
441 if (persist && write_cert_file() == -1)
442 return (-1);
444 return (0);
447 int
448 cert_delete_for(const char *cert, struct iri *iri, int persist)
450 struct ccert *c;
451 size_t i;
453 if ((c = find_cert_for(&cert_store, iri, &i)) == NULL)
454 return (-1);
456 if (!persist) {
457 c->flags |= CERT_TEMP_DEL;
458 return (0);
461 certs_delete(&cert_store, i);
462 return (write_cert_file());
465 int
466 cert_open(const char *cert)
468 char path[PATH_MAX];
469 struct stat sb;
470 int fd;
472 strlcpy(path, cert_dir, sizeof(path));
473 strlcat(path, "/", sizeof(path));
474 strlcat(path, cert, sizeof(path));
476 if ((fd = open(path, O_RDONLY)) == -1)
477 return (-1);
479 if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) {
480 close(fd);
481 return (-1);
484 return (fd);
487 static EVP_PKEY *
488 rsa_key_create(FILE *f, const char *fname)
490 EVP_PKEY_CTX *ctx = NULL;
491 EVP_PKEY *pkey = NULL;
492 int ret = -1;
494 /* First, create the context and the key. */
496 if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) == NULL)
497 goto done;
499 if (EVP_PKEY_keygen_init(ctx) <= 0)
500 goto done;
502 if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KBITS) <= 0)
503 goto done;
505 if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
506 goto done;
508 /* Serialize the key to the disc. */
509 if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL))
510 goto done;
512 ret = 0;
513 done:
514 if (ret == -1) {
515 EVP_PKEY_free(pkey);
516 pkey = NULL;
518 EVP_PKEY_CTX_free(ctx);
519 return pkey;
522 static EVP_PKEY *
523 ec_key_create(FILE *f, const char *fname)
525 EC_KEY *eckey = NULL;
526 EVP_PKEY *pkey = NULL;
527 int ret = -1;
529 if ((eckey = EC_KEY_new_by_curve_name(NID_secp384r1)) == NULL)
530 goto done;
532 if (!EC_KEY_generate_key(eckey))
533 goto done;
535 /* Serialise the key to the disc in EC format */
536 if (!PEM_write_ECPrivateKey(f, eckey, NULL, NULL, 0, NULL, NULL))
537 goto done;
539 /* Convert the EC key into a PKEY structure */
540 if ((pkey = EVP_PKEY_new()) == NULL)
541 goto done;
543 if (!EVP_PKEY_set1_EC_KEY(pkey, eckey))
544 goto done;
546 ret = 0;
547 done:
548 if (ret == -1) {
549 EVP_PKEY_free(pkey);
550 pkey = NULL;
552 EC_KEY_free(eckey);
553 return pkey;
556 int
557 cert_new(const char *common_name, const char *certpath, const char *keypath,
558 int eckey)
560 EVP_PKEY *pkey = NULL;
561 X509 *x509 = NULL;
562 X509_NAME *name = NULL;
563 FILE *fp = NULL;
564 int ret = -1;
565 const unsigned char *cn = (const unsigned char*)common_name;
567 if ((fp = fopen(keypath, "w")) == NULL)
568 goto done;
570 if (eckey)
571 pkey = ec_key_create(fp, keypath);
572 else
573 pkey = rsa_key_create(fp, keypath);
574 if (pkey == NULL)
575 goto done;
577 if (fflush(fp) == EOF || fclose(fp) == EOF)
578 goto done;
579 fp = NULL;
581 if ((x509 = X509_new()) == NULL)
582 goto done;
584 ASN1_INTEGER_set(X509_get_serialNumber(x509), 0);
585 X509_gmtime_adj(X509_get_notBefore(x509), 0);
586 X509_gmtime_adj(X509_get_notAfter(x509), 315360000L); /* 10 years */
587 X509_set_version(x509, 2); // v3
589 if (!X509_set_pubkey(x509, pkey))
590 goto done;
592 if ((name = X509_NAME_new()) == NULL)
593 goto done;
595 if (!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, cn,
596 -1, -1, 0))
597 goto done;
599 X509_set_subject_name(x509, name);
600 X509_set_issuer_name(x509, name);
602 if (!X509_sign(x509, pkey, EVP_sha256()))
603 goto done;
605 if ((fp = fopen(certpath, "w")) == NULL)
606 goto done;
608 if (!PEM_write_X509(fp, x509))
609 goto done;
611 if (fflush(fp) == EOF)
612 goto done;
614 ret = 0;
615 done:
616 if (pkey)
617 EVP_PKEY_free(pkey);
618 if (x509)
619 X509_free(x509);
620 if (name)
621 X509_NAME_free(name);
622 if (fp)
623 fclose(fp);
625 if (ret == -1) {
626 (void) unlink(certpath);
627 (void) unlink(keypath);
629 return (ret);