1*7c9b6f9dSpascal /* $OpenBSD: ikeca.c,v 1.52 2024/12/12 17:29:33 pascal Exp $ */ 2901ee4f0Sreyk 3901ee4f0Sreyk /* 43fbc3006Sreyk * Copyright (c) 2010 Jonathan Gray <jsg@openbsd.org> 5901ee4f0Sreyk * 6901ee4f0Sreyk * Permission to use, copy, modify, and distribute this software for any 7901ee4f0Sreyk * purpose with or without fee is hereby granted, provided that the above 8901ee4f0Sreyk * copyright notice and this permission notice appear in all copies. 9901ee4f0Sreyk * 10901ee4f0Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11901ee4f0Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12901ee4f0Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13901ee4f0Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14901ee4f0Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15901ee4f0Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16901ee4f0Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17901ee4f0Sreyk */ 18901ee4f0Sreyk 19901ee4f0Sreyk #include <sys/types.h> 20901ee4f0Sreyk #include <sys/stat.h> 21731c78a3Stedu #include <sys/wait.h> 22901ee4f0Sreyk #include <stdio.h> 23901ee4f0Sreyk #include <unistd.h> 24901ee4f0Sreyk #include <err.h> 25901ee4f0Sreyk #include <errno.h> 26901ee4f0Sreyk #include <string.h> 27901ee4f0Sreyk #include <stdlib.h> 28901ee4f0Sreyk #include <pwd.h> 29901ee4f0Sreyk #include <fcntl.h> 30901ee4f0Sreyk #include <fts.h> 31901ee4f0Sreyk #include <dirent.h> 32b9fc9a72Sderaadt #include <limits.h> 33901ee4f0Sreyk 34901ee4f0Sreyk #include <openssl/rand.h> 35901ee4f0Sreyk #include <openssl/rsa.h> 36901ee4f0Sreyk #include <openssl/pem.h> 37901ee4f0Sreyk 38477ac106Sderaadt #include "types.h" 39901ee4f0Sreyk #include "parser.h" 40901ee4f0Sreyk 41ddb14f44Sreyk #ifndef PREFIX 42ddb14f44Sreyk #define PREFIX "" 43ddb14f44Sreyk #endif 44ddb14f44Sreyk #ifndef SSLDIR 45ddb14f44Sreyk #define SSLDIR PREFIX "/etc/ssl" 46ddb14f44Sreyk #endif 47ddb14f44Sreyk #define SSL_CNF SSLDIR "/openssl.cnf" 48ddb14f44Sreyk #define X509_CNF SSLDIR "/x509v3.cnf" 49ddb14f44Sreyk #define IKECA_CNF SSLDIR "/ikeca.cnf" 50ddb14f44Sreyk #define KEYBASE PREFIX "/etc/iked" 51ddb14f44Sreyk #ifndef EXPDIR 52ddb14f44Sreyk #define EXPDIR PREFIX "/usr/share/iked" 53ddb14f44Sreyk #endif 54901ee4f0Sreyk 55ddb14f44Sreyk #ifndef PATH_OPENSSL 56dab3f910Sjsing #define PATH_OPENSSL "/usr/bin/openssl" 57ddb14f44Sreyk #endif 58ddb14f44Sreyk #ifndef PATH_ZIP 59901ee4f0Sreyk #define PATH_ZIP "/usr/local/bin/zip" 60ddb14f44Sreyk #endif 61ddb14f44Sreyk #ifndef PATH_TAR 62901ee4f0Sreyk #define PATH_TAR "/bin/tar" 63ddb14f44Sreyk #endif 64901ee4f0Sreyk 65901ee4f0Sreyk struct ca { 66901ee4f0Sreyk char sslpath[PATH_MAX]; 67731c78a3Stedu char passfile[PATH_MAX + 5]; /* Includes the "file:" prefix */ 688e3cf88fSjsg char index[PATH_MAX]; 698e3cf88fSjsg char serial[PATH_MAX]; 70901ee4f0Sreyk char sslcnf[PATH_MAX]; 71901ee4f0Sreyk char extcnf[PATH_MAX]; 72731c78a3Stedu char *batch; 73901ee4f0Sreyk char *caname; 74901ee4f0Sreyk }; 75901ee4f0Sreyk 767638a50cSjsg struct { 777638a50cSjsg char *dir; 787638a50cSjsg mode_t mode; 792746af23Sjsg } hier[] = { 802746af23Sjsg { "", 0755 }, 817638a50cSjsg { "/ca", 0755 }, 827638a50cSjsg { "/certs", 0755 }, 837638a50cSjsg { "/crls", 0755 }, 847638a50cSjsg { "/export", 0755 }, 857638a50cSjsg { "/private", 0700 } 867638a50cSjsg }; 877638a50cSjsg 884f23bdabSreyk /* explicitly list allowed variables */ 898068c079Spatrick char *ca_env[][2] = { 904f23bdabSreyk { "$ENV::CADB", NULL }, 918e3cf88fSjsg { "$ENV::CASERIAL", NULL }, 924f23bdabSreyk { "$ENV::CERTFQDN", NULL }, 934f23bdabSreyk { "$ENV::CERTIP", NULL }, 944f23bdabSreyk { "$ENV::CERTPATHLEN", NULL }, 954f23bdabSreyk { "$ENV::CERTUSAGE", NULL }, 964f23bdabSreyk { "$ENV::CERT_C", NULL }, 974f23bdabSreyk { "$ENV::CERT_CN", NULL }, 984f23bdabSreyk { "$ENV::CERT_EMAIL", NULL }, 994f23bdabSreyk { "$ENV::CERT_L", NULL }, 1004f23bdabSreyk { "$ENV::CERT_O", NULL }, 1014f23bdabSreyk { "$ENV::CERT_OU", NULL }, 1024f23bdabSreyk { "$ENV::CERT_ST", NULL }, 1034f23bdabSreyk { "$ENV::EXTCERTUSAGE", NULL }, 1044f23bdabSreyk { "$ENV::NSCERTTYPE", NULL }, 10537c7452dSsthen { "$ENV::REQ_EXT", NULL }, 1064f23bdabSreyk { NULL } 1074f23bdabSreyk }; 1084f23bdabSreyk 1094f23bdabSreyk int ca_sign(struct ca *, char *, int); 11037c7452dSsthen int ca_request(struct ca *, char *, int); 11147e28f79Sreyk void ca_newpass(char *, char *); 1123da6623bSjsg int fcopy(char *, char *, mode_t); 113bfcdcf42Sreyk void fcopy_env(const char *, const char *, mode_t); 114901ee4f0Sreyk int rm_dir(char *); 11547e28f79Sreyk void ca_hier(char *); 1164f23bdabSreyk void ca_setenv(const char *, const char *); 1174f23bdabSreyk void ca_clrenv(void); 1184f23bdabSreyk void ca_setcnf(struct ca *, const char *); 1198e3cf88fSjsg void ca_create_index(struct ca *); 120731c78a3Stedu int static ca_execv(char *const []); 1214f23bdabSreyk 1224f23bdabSreyk /* util.c */ 1234f23bdabSreyk int expand_string(char *, size_t, const char *, const char *); 124901ee4f0Sreyk 125901ee4f0Sreyk int 126901ee4f0Sreyk ca_delete(struct ca *ca) 127901ee4f0Sreyk { 128901ee4f0Sreyk return (rm_dir(ca->sslpath)); 129901ee4f0Sreyk } 130901ee4f0Sreyk 131901ee4f0Sreyk int 1321dbb1d4aSjsg ca_key_create(struct ca *ca, char *keyname) 133901ee4f0Sreyk { 1341dbb1d4aSjsg struct stat st; 135901ee4f0Sreyk char path[PATH_MAX]; 136c03e9c27Stobhe int len; 137901ee4f0Sreyk 138c03e9c27Stobhe len = snprintf(path, sizeof(path), "%s/private/%s.key", 139c03e9c27Stobhe ca->sslpath, keyname); 140c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(path)) 141c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1421dbb1d4aSjsg 1431dbb1d4aSjsg /* don't recreate key if one is already present */ 1441dbb1d4aSjsg if (stat(path, &st) == 0) { 1451dbb1d4aSjsg return (0); 1461dbb1d4aSjsg } 147901ee4f0Sreyk 148731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "genrsa", "-out", path, "2048", NULL }; 149731c78a3Stedu ca_execv(cmd); 150901ee4f0Sreyk chmod(path, 0600); 151901ee4f0Sreyk 152901ee4f0Sreyk return (0); 153901ee4f0Sreyk } 154901ee4f0Sreyk 155901ee4f0Sreyk int 1561dbb1d4aSjsg ca_key_import(struct ca *ca, char *keyname, char *import) 1571dbb1d4aSjsg { 1581dbb1d4aSjsg struct stat st; 1591dbb1d4aSjsg char dst[PATH_MAX]; 160c03e9c27Stobhe int len; 1611dbb1d4aSjsg 1621dbb1d4aSjsg if (stat(import, &st) != 0) { 1631dbb1d4aSjsg warn("could not access keyfile %s", import); 1641dbb1d4aSjsg return (1); 1651dbb1d4aSjsg } 1661dbb1d4aSjsg 167c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/private/%s.key", ca->sslpath, keyname); 168c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 169c03e9c27Stobhe err(1, "%s: snprintf", __func__); 170c03e9c27Stobhe 1711dbb1d4aSjsg fcopy(import, dst, 0600); 1721dbb1d4aSjsg 1731dbb1d4aSjsg return (0); 1741dbb1d4aSjsg } 1751dbb1d4aSjsg 1761dbb1d4aSjsg int 1771dbb1d4aSjsg ca_key_delete(struct ca *ca, char *keyname) 1781dbb1d4aSjsg { 1791dbb1d4aSjsg char path[PATH_MAX]; 180c03e9c27Stobhe int len; 1811dbb1d4aSjsg 182c03e9c27Stobhe len = snprintf(path, sizeof(path), "%s/private/%s.key", 183c03e9c27Stobhe ca->sslpath, keyname); 184c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(path)) 185c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1861dbb1d4aSjsg unlink(path); 1871dbb1d4aSjsg 1881dbb1d4aSjsg return (0); 1891dbb1d4aSjsg } 1901dbb1d4aSjsg 1911dbb1d4aSjsg int 192901ee4f0Sreyk ca_delkey(struct ca *ca, char *keyname) 193901ee4f0Sreyk { 194901ee4f0Sreyk char file[PATH_MAX]; 195c03e9c27Stobhe int len; 196901ee4f0Sreyk 197c03e9c27Stobhe len = snprintf(file, sizeof(file), "%s/%s.crt", ca->sslpath, keyname); 198c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(file)) 199c03e9c27Stobhe err(1, "%s: snprintf", __func__); 200901ee4f0Sreyk unlink(file); 201901ee4f0Sreyk 202c03e9c27Stobhe len = snprintf(file, sizeof(file), "%s/private/%s.key", ca->sslpath, keyname); 203c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(file)) 204c03e9c27Stobhe err(1, "%s: snprintf", __func__); 205901ee4f0Sreyk unlink(file); 206901ee4f0Sreyk 207c03e9c27Stobhe len = snprintf(file, sizeof(file), "%s/private/%s.csr", ca->sslpath, keyname); 208c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(file)) 209c03e9c27Stobhe err(1, "%s: snprintf", __func__); 210901ee4f0Sreyk unlink(file); 211901ee4f0Sreyk 212c03e9c27Stobhe len = snprintf(file, sizeof(file), "%s/private/%s.pfx", ca->sslpath, keyname); 213c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(file)) 214c03e9c27Stobhe err(1, "%s: snprintf", __func__); 215901ee4f0Sreyk unlink(file); 216901ee4f0Sreyk 217901ee4f0Sreyk return (0); 218901ee4f0Sreyk } 219901ee4f0Sreyk 220901ee4f0Sreyk int 22137c7452dSsthen ca_request(struct ca *ca, char *keyname, int type) 222901ee4f0Sreyk { 22337c7452dSsthen char hostname[HOST_NAME_MAX+1]; 22437c7452dSsthen char name[128]; 225731c78a3Stedu char key[PATH_MAX]; 226901ee4f0Sreyk char path[PATH_MAX]; 227c03e9c27Stobhe int len; 228901ee4f0Sreyk 2294f23bdabSreyk ca_setenv("$ENV::CERT_CN", keyname); 23037c7452dSsthen 23137c7452dSsthen strlcpy(name, keyname, sizeof(name)); 23237c7452dSsthen 23337c7452dSsthen if (type == HOST_IPADDR) { 23437c7452dSsthen ca_setenv("$ENV::CERTIP", name); 23537c7452dSsthen ca_setenv("$ENV::REQ_EXT", "x509v3_IPAddr"); 23637c7452dSsthen } else if (type == HOST_FQDN) { 23737c7452dSsthen if (!strcmp(keyname, "local")) { 23837c7452dSsthen if (gethostname(hostname, sizeof(hostname))) 23937c7452dSsthen err(1, "gethostname"); 24037c7452dSsthen strlcpy(name, hostname, sizeof(name)); 24137c7452dSsthen } 24237c7452dSsthen ca_setenv("$ENV::CERTFQDN", name); 24337c7452dSsthen ca_setenv("$ENV::REQ_EXT", "x509v3_FQDN"); 24437c7452dSsthen } else { 24537c7452dSsthen errx(1, "unknown host type %d", type); 24637c7452dSsthen } 24737c7452dSsthen 2484f23bdabSreyk ca_setcnf(ca, keyname); 2494f23bdabSreyk 250c03e9c27Stobhe len = snprintf(key, sizeof(key), "%s/private/%s.key", ca->sslpath, keyname); 251c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(key)) 252c03e9c27Stobhe err(1, "%s: snprintf", __func__); 253c03e9c27Stobhe len = snprintf(path, sizeof(path), "%s/private/%s.csr", ca->sslpath, keyname); 254c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(path)) 255c03e9c27Stobhe err(1, "%s: snprintf", __func__); 256cfd26ebdSreyk 257731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "req", "-new", "-key", key, "-out", path, 258731c78a3Stedu "-config", ca->sslcnf, ca->batch, NULL }; 259731c78a3Stedu ca_execv(cmd); 260901ee4f0Sreyk chmod(path, 0600); 261901ee4f0Sreyk 262901ee4f0Sreyk return (0); 263901ee4f0Sreyk } 264901ee4f0Sreyk 265901ee4f0Sreyk int 2664f23bdabSreyk ca_sign(struct ca *ca, char *keyname, int type) 267901ee4f0Sreyk { 268731c78a3Stedu char cakey[PATH_MAX]; 269731c78a3Stedu char cacrt[PATH_MAX]; 270731c78a3Stedu char out[PATH_MAX]; 271731c78a3Stedu char in[PATH_MAX]; 272731c78a3Stedu char *extensions = NULL; 273c03e9c27Stobhe int len; 274901ee4f0Sreyk 275901ee4f0Sreyk if (type == HOST_IPADDR) { 2764f23bdabSreyk extensions = "x509v3_IPAddr"; 277901ee4f0Sreyk } else if (type == HOST_FQDN) { 2784f23bdabSreyk extensions = "x509v3_FQDN"; 2794f23bdabSreyk } else { 2804f23bdabSreyk errx(1, "unknown host type %d", type); 2814f23bdabSreyk } 2824f23bdabSreyk 2838e3cf88fSjsg ca_create_index(ca); 2848e3cf88fSjsg 2858e3cf88fSjsg ca_setenv("$ENV::CADB", ca->index); 2868e3cf88fSjsg ca_setenv("$ENV::CASERIAL", ca->serial); 2874f23bdabSreyk ca_setcnf(ca, keyname); 2884f23bdabSreyk 289c03e9c27Stobhe len = snprintf(cakey, sizeof(cakey), "%s/private/ca.key", ca->sslpath); 290c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(cakey)) 291c03e9c27Stobhe err(1, "%s: snprintf", __func__); 292c03e9c27Stobhe len = snprintf(cacrt, sizeof(cacrt), "%s/ca.crt", ca->sslpath); 293c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(cacrt)) 294c03e9c27Stobhe err(1, "%s: snprintf", __func__); 295c03e9c27Stobhe len = snprintf(out, sizeof(out), "%s/%s.crt", ca->sslpath, keyname); 296c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(out)) 297c03e9c27Stobhe err(1, "%s: snprintf", __func__); 298c03e9c27Stobhe len = snprintf(in, sizeof(in), "%s/private/%s.csr", ca->sslpath, keyname); 299c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(in)) 300c03e9c27Stobhe err(1, "%s: snprintf", __func__); 301901ee4f0Sreyk 302731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "ca", "-config", ca->sslcnf, 303731c78a3Stedu "-keyfile", cakey, "-cert", cacrt, "-extfile", ca->extcnf, 304731c78a3Stedu "-extensions", extensions, "-out", out, "-in", in, 305731c78a3Stedu "-passin", ca->passfile, "-outdir", ca->sslpath, "-batch", NULL }; 306731c78a3Stedu ca_execv(cmd); 307901ee4f0Sreyk 308901ee4f0Sreyk return (0); 309901ee4f0Sreyk } 310901ee4f0Sreyk 311901ee4f0Sreyk int 312cfd26ebdSreyk ca_certificate(struct ca *ca, char *keyname, int type, int action) 313901ee4f0Sreyk { 3144f23bdabSreyk ca_clrenv(); 315cfd26ebdSreyk 316cfd26ebdSreyk switch (action) { 317cfd26ebdSreyk case CA_SERVER: 3184f23bdabSreyk ca_setenv("$ENV::EXTCERTUSAGE", "serverAuth"); 3194f23bdabSreyk ca_setenv("$ENV::NSCERTTYPE", "server"); 3204f23bdabSreyk ca_setenv("$ENV::CERTUSAGE", 3214f23bdabSreyk "digitalSignature,keyEncipherment"); 322cfd26ebdSreyk break; 323cfd26ebdSreyk case CA_CLIENT: 3244f23bdabSreyk ca_setenv("$ENV::EXTCERTUSAGE", "clientAuth"); 3254f23bdabSreyk ca_setenv("$ENV::NSCERTTYPE", "client"); 3264f23bdabSreyk ca_setenv("$ENV::CERTUSAGE", 3274f23bdabSreyk "digitalSignature,keyAgreement"); 328cfd26ebdSreyk break; 329ab7171b1Sjsg case CA_OCSP: 330ab7171b1Sjsg ca_setenv("$ENV::EXTCERTUSAGE", "OCSPSigning"); 331ab7171b1Sjsg ca_setenv("$ENV::CERTUSAGE", 332ab7171b1Sjsg "nonRepudiation,digitalSignature,keyEncipherment"); 333ab7171b1Sjsg break; 334cfd26ebdSreyk default: 335cfd26ebdSreyk break; 336cfd26ebdSreyk } 337cfd26ebdSreyk 3381dbb1d4aSjsg ca_key_create(ca, keyname); 33937c7452dSsthen ca_request(ca, keyname, type); 3404f23bdabSreyk ca_sign(ca, keyname, type); 341901ee4f0Sreyk 342901ee4f0Sreyk return (0); 343901ee4f0Sreyk } 344901ee4f0Sreyk 345901ee4f0Sreyk int 3467638a50cSjsg ca_key_install(struct ca *ca, char *keyname, char *dir) 347901ee4f0Sreyk { 348901ee4f0Sreyk struct stat st; 349901ee4f0Sreyk char src[PATH_MAX]; 350901ee4f0Sreyk char dst[PATH_MAX]; 351731c78a3Stedu char out[PATH_MAX]; 3527638a50cSjsg char *p = NULL; 353c03e9c27Stobhe int len; 354901ee4f0Sreyk 355c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/private/%s.key", ca->sslpath, keyname); 356c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 357c03e9c27Stobhe err(1, "%s: snprintf", __func__); 358901ee4f0Sreyk if (stat(src, &st) == -1) { 359901ee4f0Sreyk if (errno == ENOENT) 360901ee4f0Sreyk printf("key for '%s' does not exist\n", ca->caname); 361901ee4f0Sreyk else 362901ee4f0Sreyk warn("could not access key"); 363901ee4f0Sreyk return (1); 364901ee4f0Sreyk } 365901ee4f0Sreyk 3667638a50cSjsg if (dir == NULL) 3677638a50cSjsg p = dir = strdup(KEYBASE); 3687638a50cSjsg 3697638a50cSjsg ca_hier(dir); 3707638a50cSjsg 371c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/private/local.key", dir); 372c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 373c03e9c27Stobhe err(1, "%s: snprintf", __func__); 3743da6623bSjsg fcopy(src, dst, 0600); 375901ee4f0Sreyk 376c03e9c27Stobhe len = snprintf(out, sizeof(out), "%s/local.pub", dir); 377c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(out)) 378c03e9c27Stobhe err(1, "%s: snprintf", __func__); 379731c78a3Stedu 380731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "rsa", "-out", out, "-in", dst, 381731c78a3Stedu "-pubout", NULL }; 382731c78a3Stedu ca_execv(cmd); 383901ee4f0Sreyk 3847638a50cSjsg free(p); 3851dbb1d4aSjsg 3860dd4c7c3Sjsg return (0); 3871dbb1d4aSjsg } 3881dbb1d4aSjsg 3891dbb1d4aSjsg int 3907638a50cSjsg ca_cert_install(struct ca *ca, char *keyname, char *dir) 3911dbb1d4aSjsg { 3921dbb1d4aSjsg char src[PATH_MAX]; 3931dbb1d4aSjsg char dst[PATH_MAX]; 3941dbb1d4aSjsg int r; 3957638a50cSjsg char *p = NULL; 396c03e9c27Stobhe int len; 3971dbb1d4aSjsg 3987638a50cSjsg if (dir == NULL) 3997638a50cSjsg p = dir = strdup(KEYBASE); 4007638a50cSjsg 4017638a50cSjsg ca_hier(dir); 4027638a50cSjsg 4037638a50cSjsg if ((r = ca_key_install(ca, keyname, dir)) != 0) { 4047638a50cSjsg free(dir); 4051dbb1d4aSjsg return (r); 4067638a50cSjsg } 4071dbb1d4aSjsg 408c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/%s.crt", ca->sslpath, keyname); 409c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 410c03e9c27Stobhe err(1, "%s: snprintf", __func__); 411c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/certs/%s.crt", dir, keyname); 412c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 413c03e9c27Stobhe err(1, "%s: snprintf", __func__); 4143da6623bSjsg fcopy(src, dst, 0644); 4153da6623bSjsg 4167638a50cSjsg free(p); 4177638a50cSjsg 418901ee4f0Sreyk return (0); 419901ee4f0Sreyk } 420901ee4f0Sreyk 42147e28f79Sreyk void 422cfd26ebdSreyk ca_newpass(char *passfile, char *password) 423901ee4f0Sreyk { 424901ee4f0Sreyk FILE *f; 425901ee4f0Sreyk char *pass; 426901ee4f0Sreyk char prev[_PASSWORD_LEN + 1]; 427901ee4f0Sreyk 428cfd26ebdSreyk if (password != NULL) { 429cfd26ebdSreyk pass = password; 430cfd26ebdSreyk goto done; 431cfd26ebdSreyk } 432cfd26ebdSreyk 433901ee4f0Sreyk pass = getpass("CA passphrase:"); 434901ee4f0Sreyk if (pass == NULL || *pass == '\0') 435901ee4f0Sreyk err(1, "password not set"); 436901ee4f0Sreyk 437901ee4f0Sreyk strlcpy(prev, pass, sizeof(prev)); 438901ee4f0Sreyk pass = getpass("Retype CA passphrase:"); 439901ee4f0Sreyk if (pass == NULL || strcmp(prev, pass) != 0) 440901ee4f0Sreyk errx(1, "passphrase does not match!"); 441901ee4f0Sreyk 442cfd26ebdSreyk done: 443901ee4f0Sreyk if ((f = fopen(passfile, "wb")) == NULL) 444901ee4f0Sreyk err(1, "could not open passfile %s", passfile); 445901ee4f0Sreyk chmod(passfile, 0600); 446901ee4f0Sreyk 447901ee4f0Sreyk fprintf(f, "%s\n%s\n", pass, pass); 448901ee4f0Sreyk 449901ee4f0Sreyk fclose(f); 450901ee4f0Sreyk } 451901ee4f0Sreyk 452901ee4f0Sreyk int 453901ee4f0Sreyk ca_create(struct ca *ca) 454901ee4f0Sreyk { 455731c78a3Stedu char key[PATH_MAX]; 456731c78a3Stedu char csr[PATH_MAX]; 457731c78a3Stedu char crt[PATH_MAX]; 458c03e9c27Stobhe int len; 459901ee4f0Sreyk 4604f23bdabSreyk ca_clrenv(); 4614f23bdabSreyk 462c03e9c27Stobhe len = snprintf(key, sizeof(key), "%s/private/ca.key", ca->sslpath); 463c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(key)) 464c03e9c27Stobhe err(1, "%s: snprintf", __func__); 465731c78a3Stedu char *genrsa[] = { PATH_OPENSSL, "genrsa", "-aes256", "-out", key, 466731c78a3Stedu "-passout", ca->passfile, "2048", NULL }; 467731c78a3Stedu ca_execv(genrsa); 468731c78a3Stedu 469731c78a3Stedu chmod(key, 0600); 470901ee4f0Sreyk 4714f23bdabSreyk ca_setenv("$ENV::CERT_CN", "VPN CA"); 4727cc3ce1dSsthen ca_setenv("$ENV::REQ_EXT", "x509v3_CA"); 4734f23bdabSreyk ca_setcnf(ca, "ca"); 4744f23bdabSreyk 475c03e9c27Stobhe len = snprintf(csr, sizeof(csr), "%s/private/ca.csr", ca->sslpath); 476c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(csr)) 477c03e9c27Stobhe err(1, "%s: snprintf", __func__); 478731c78a3Stedu char *reqcmd[] = { PATH_OPENSSL, "req", "-new", "-key", key, 479731c78a3Stedu "-config", ca->sslcnf, "-out", csr, 480731c78a3Stedu "-passin", ca->passfile, ca->batch, NULL }; 481731c78a3Stedu ca_execv(reqcmd); 482731c78a3Stedu chmod(csr, 0600); 483901ee4f0Sreyk 484c03e9c27Stobhe len = snprintf(crt, sizeof(crt), "%s/ca.crt", ca->sslpath); 485c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(crt)) 486c03e9c27Stobhe err(1, "%s: snprintf", __func__); 487731c78a3Stedu char *x509[] = { PATH_OPENSSL, "x509", "-req", "-days", "4500", 488731c78a3Stedu "-in", csr, "-signkey", key, "-sha256", 489731c78a3Stedu "-extfile", ca->extcnf, "-extensions", "x509v3_CA", 490731c78a3Stedu "-out", crt, "-passin", ca->passfile, NULL }; 491731c78a3Stedu ca_execv(x509); 492901ee4f0Sreyk 4937430271cSphessler /* Create the CRL revocation list */ 4947430271cSphessler ca_revoke(ca, NULL); 4957430271cSphessler 496901ee4f0Sreyk return (0); 497901ee4f0Sreyk } 498901ee4f0Sreyk 499901ee4f0Sreyk int 5007638a50cSjsg ca_install(struct ca *ca, char *dir) 501901ee4f0Sreyk { 502901ee4f0Sreyk struct stat st; 503901ee4f0Sreyk char src[PATH_MAX]; 504901ee4f0Sreyk char dst[PATH_MAX]; 5057638a50cSjsg char *p = NULL; 506c03e9c27Stobhe int len; 507901ee4f0Sreyk 508c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/ca.crt", ca->sslpath); 509c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 510c03e9c27Stobhe err(1, "%s: snprintf", __func__); 511901ee4f0Sreyk if (stat(src, &st) == -1) { 512901ee4f0Sreyk printf("CA '%s' does not exist\n", ca->caname); 513901ee4f0Sreyk return (1); 514901ee4f0Sreyk } 515901ee4f0Sreyk 5167638a50cSjsg if (dir == NULL) 5177638a50cSjsg p = dir = strdup(KEYBASE); 5187638a50cSjsg 5197638a50cSjsg ca_hier(dir); 5207638a50cSjsg 521c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/ca/ca.crt", dir); 522c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 523c03e9c27Stobhe err(1, "%s: snprintf", __func__); 5243da6623bSjsg if (fcopy(src, dst, 0644) == 0) 525c3cc2c5eSjsg printf("certificate for CA '%s' installed into %s\n", 526c3cc2c5eSjsg ca->caname, dst); 527c3cc2c5eSjsg 528c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/ca.crl", ca->sslpath); 529c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 530c03e9c27Stobhe err(1, "%s: snprintf", __func__); 531c3cc2c5eSjsg if (stat(src, &st) == 0) { 532c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/crls/ca.crl", dir); 533c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 534c03e9c27Stobhe err(1, "%s: snprintf", __func__); 535c3cc2c5eSjsg if (fcopy(src, dst, 0644) == 0) 536c3cc2c5eSjsg printf("CRL for CA '%s' installed to %s\n", 537c3cc2c5eSjsg ca->caname, dst); 538c3cc2c5eSjsg } 539901ee4f0Sreyk 5407638a50cSjsg free(p); 5417638a50cSjsg 542901ee4f0Sreyk return (0); 543901ee4f0Sreyk } 544901ee4f0Sreyk 545901ee4f0Sreyk int 54669ffd282Sreyk ca_show_certs(struct ca *ca, char *name) 547901ee4f0Sreyk { 548901ee4f0Sreyk DIR *dir; 549901ee4f0Sreyk struct dirent *de; 550901ee4f0Sreyk char path[PATH_MAX]; 551901ee4f0Sreyk char *p; 55269ffd282Sreyk struct stat st; 553c03e9c27Stobhe int len; 55469ffd282Sreyk 55569ffd282Sreyk if (name != NULL) { 556c03e9c27Stobhe len = snprintf(path, sizeof(path), "%s/%s.crt", 55769ffd282Sreyk ca->sslpath, name); 558c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(path)) 559c03e9c27Stobhe err(1, "%s: snprintf", __func__); 56069ffd282Sreyk if (stat(path, &st) != 0) 56169ffd282Sreyk err(1, "could not open file %s.crt", name); 562731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "x509", "-text", 563731c78a3Stedu "-in", path, NULL }; 564731c78a3Stedu ca_execv(cmd); 56569ffd282Sreyk printf("\n"); 56669ffd282Sreyk return (0); 56769ffd282Sreyk } 568901ee4f0Sreyk 569901ee4f0Sreyk if ((dir = opendir(ca->sslpath)) == NULL) 570901ee4f0Sreyk err(1, "could not open directory %s", ca->sslpath); 571901ee4f0Sreyk 572901ee4f0Sreyk while ((de = readdir(dir)) != NULL) { 573901ee4f0Sreyk if (de->d_namlen > 4) { 574901ee4f0Sreyk p = de->d_name + de->d_namlen - 4; 575901ee4f0Sreyk if (strcmp(".crt", p) != 0) 576901ee4f0Sreyk continue; 577c03e9c27Stobhe len = snprintf(path, sizeof(path), "%s/%s", ca->sslpath, 578901ee4f0Sreyk de->d_name); 579c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(path)) 580c03e9c27Stobhe err(1, "%s: snprintf", __func__); 581731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "x509", "-subject", 582731c78a3Stedu "-fingerprint", "-dates", "-noout", "-in", path, 583731c78a3Stedu NULL }; 584731c78a3Stedu ca_execv(cmd); 585901ee4f0Sreyk printf("\n"); 586901ee4f0Sreyk } 587901ee4f0Sreyk } 588901ee4f0Sreyk 589901ee4f0Sreyk closedir(dir); 590901ee4f0Sreyk 591901ee4f0Sreyk return (0); 592901ee4f0Sreyk } 593901ee4f0Sreyk 594901ee4f0Sreyk int 5953da6623bSjsg fcopy(char *src, char *dst, mode_t mode) 596901ee4f0Sreyk { 597901ee4f0Sreyk int ifd, ofd; 598ef316c85Sreyk uint8_t buf[BUFSIZ]; 599901ee4f0Sreyk ssize_t r; 600901ee4f0Sreyk 601901ee4f0Sreyk if ((ifd = open(src, O_RDONLY)) == -1) 602901ee4f0Sreyk err(1, "open %s", src); 603901ee4f0Sreyk 6041dbb1d4aSjsg if ((ofd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { 605ffb4dd05Sguenther int saved_errno = errno; 606901ee4f0Sreyk close(ifd); 607ffb4dd05Sguenther errc(1, saved_errno, "open %s", dst); 608901ee4f0Sreyk } 609901ee4f0Sreyk 610901ee4f0Sreyk while ((r = read(ifd, buf, sizeof(buf))) > 0) { 6112a5bbc1bStobhe if (write(ofd, buf, r) == -1) 6122a5bbc1bStobhe err(1, "%s: write", __func__); 613901ee4f0Sreyk } 614901ee4f0Sreyk 615901ee4f0Sreyk close(ofd); 616901ee4f0Sreyk close(ifd); 617901ee4f0Sreyk 618901ee4f0Sreyk return (r == -1); 619901ee4f0Sreyk } 620901ee4f0Sreyk 621bfcdcf42Sreyk void 6224f23bdabSreyk fcopy_env(const char *src, const char *dst, mode_t mode) 6234f23bdabSreyk { 6244f23bdabSreyk int ofd = -1, i; 625ef316c85Sreyk uint8_t buf[BUFSIZ]; 6264f23bdabSreyk ssize_t r = -1, len; 6274f23bdabSreyk FILE *ifp = NULL; 6284f23bdabSreyk int saved_errno; 6294f23bdabSreyk 6304f23bdabSreyk if ((ifp = fopen(src, "r")) == NULL) 6314f23bdabSreyk err(1, "fopen %s", src); 6324f23bdabSreyk 6334f23bdabSreyk if ((ofd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) 6344f23bdabSreyk goto done; 6354f23bdabSreyk 6364f23bdabSreyk while (fgets(buf, sizeof(buf), ifp) != NULL) { 6374f23bdabSreyk for (i = 0; ca_env[i][0] != NULL; i++) { 6384f23bdabSreyk if (ca_env[i][1] == NULL) 6394f23bdabSreyk continue; 6404f23bdabSreyk if (expand_string(buf, sizeof(buf), 6414f23bdabSreyk ca_env[i][0], ca_env[i][1]) == -1) 6424f23bdabSreyk errx(1, "env %s value too long", ca_env[i][0]); 6434f23bdabSreyk } 6444f23bdabSreyk len = strlen(buf); 6454f23bdabSreyk if (write(ofd, buf, len) != len) 6464f23bdabSreyk goto done; 6474f23bdabSreyk } 6484f23bdabSreyk 6494f23bdabSreyk r = 0; 6504f23bdabSreyk 6514f23bdabSreyk done: 6524f23bdabSreyk saved_errno = errno; 6534f23bdabSreyk close(ofd); 6544f23bdabSreyk if (ifp != NULL) 6554f23bdabSreyk fclose(ifp); 6564f23bdabSreyk if (r == -1) 6574f23bdabSreyk errc(1, saved_errno, "open %s", dst); 6584f23bdabSreyk } 6594f23bdabSreyk 6604f23bdabSreyk int 661901ee4f0Sreyk rm_dir(char *path) 662901ee4f0Sreyk { 663901ee4f0Sreyk FTS *fts; 664901ee4f0Sreyk FTSENT *p; 665901ee4f0Sreyk static char *fpath[] = { NULL, NULL }; 666901ee4f0Sreyk 667901ee4f0Sreyk fpath[0] = path; 668901ee4f0Sreyk if ((fts = fts_open(fpath, FTS_PHYSICAL, NULL)) == NULL) { 669901ee4f0Sreyk warn("fts_open %s", path); 670901ee4f0Sreyk return (1); 671901ee4f0Sreyk } 672901ee4f0Sreyk 673901ee4f0Sreyk while ((p = fts_read(fts)) != NULL) { 674901ee4f0Sreyk switch (p->fts_info) { 675901ee4f0Sreyk case FTS_DP: 676901ee4f0Sreyk case FTS_DNR: 677901ee4f0Sreyk if (rmdir(p->fts_accpath) == -1) 678901ee4f0Sreyk warn("rmdir %s", p->fts_accpath); 679901ee4f0Sreyk break; 680901ee4f0Sreyk case FTS_F: 681901ee4f0Sreyk if (unlink(p->fts_accpath) == -1) 682901ee4f0Sreyk warn("unlink %s", p->fts_accpath); 683901ee4f0Sreyk break; 684901ee4f0Sreyk case FTS_D: 685901ee4f0Sreyk case FTS_DOT: 686901ee4f0Sreyk default: 687901ee4f0Sreyk continue; 688901ee4f0Sreyk } 689901ee4f0Sreyk } 690901ee4f0Sreyk fts_close(fts); 691901ee4f0Sreyk 692901ee4f0Sreyk return (0); 693901ee4f0Sreyk } 694901ee4f0Sreyk 69547e28f79Sreyk void 6967638a50cSjsg ca_hier(char *path) 6977638a50cSjsg { 6987638a50cSjsg struct stat st; 6997638a50cSjsg char dst[PATH_MAX]; 700ef316c85Sreyk unsigned int i; 7017638a50cSjsg 7022746af23Sjsg for (i = 0; i < nitems(hier); i++) { 7037638a50cSjsg strlcpy(dst, path, sizeof(dst)); 7042746af23Sjsg strlcat(dst, hier[i].dir, sizeof(dst)); 7057638a50cSjsg if (stat(dst, &st) != 0 && errno == ENOENT && 7062746af23Sjsg mkdir(dst, hier[i].mode) != 0) 7077638a50cSjsg err(1, "failed to create dir %s", dst); 7087638a50cSjsg } 7097638a50cSjsg } 7107638a50cSjsg 711901ee4f0Sreyk int 712cfe372e4Sreyk ca_export(struct ca *ca, char *keyname, char *myname, char *password) 713901ee4f0Sreyk { 7140dd4c7c3Sjsg DIR *dexp; 7150dd4c7c3Sjsg struct dirent *de; 716901ee4f0Sreyk struct stat st; 717901ee4f0Sreyk char *pass; 718901ee4f0Sreyk char prev[_PASSWORD_LEN + 1]; 719731c78a3Stedu char passenv[_PASSWORD_LEN + 8]; 720901ee4f0Sreyk char oname[PATH_MAX]; 721901ee4f0Sreyk char src[PATH_MAX]; 722901ee4f0Sreyk char dst[PATH_MAX]; 723731c78a3Stedu char cacrt[PATH_MAX]; 724731c78a3Stedu char capfx[PATH_MAX]; 725731c78a3Stedu char key[PATH_MAX]; 726731c78a3Stedu char crt[PATH_MAX]; 727731c78a3Stedu char pfx[PATH_MAX]; 728901ee4f0Sreyk char *p; 729901ee4f0Sreyk char tpl[] = "/tmp/ikectl.XXXXXXXXXX"; 730ef316c85Sreyk unsigned int i; 7310dd4c7c3Sjsg int fd; 732c03e9c27Stobhe int len; 7330dd4c7c3Sjsg 734f614af3bSjsg if (keyname != NULL) { 7350dd4c7c3Sjsg if (strlcpy(oname, keyname, sizeof(oname)) >= sizeof(oname)) 73612d9f607Ssemarie errx(1, "name too long"); 737f614af3bSjsg } else { 738f614af3bSjsg strlcpy(oname, "ca", sizeof(oname)); 739f614af3bSjsg } 740901ee4f0Sreyk 741901ee4f0Sreyk /* colons are not valid characters in windows filenames... */ 742901ee4f0Sreyk while ((p = strchr(oname, ':')) != NULL) 743901ee4f0Sreyk *p = '_'; 744901ee4f0Sreyk 745cfe372e4Sreyk if (password != NULL) 746cfe372e4Sreyk pass = password; 747cfe372e4Sreyk else { 748901ee4f0Sreyk pass = getpass("Export passphrase:"); 749901ee4f0Sreyk if (pass == NULL || *pass == '\0') 750901ee4f0Sreyk err(1, "password not set"); 751901ee4f0Sreyk 752901ee4f0Sreyk strlcpy(prev, pass, sizeof(prev)); 753901ee4f0Sreyk pass = getpass("Retype export passphrase:"); 754901ee4f0Sreyk if (pass == NULL || strcmp(prev, pass) != 0) 755901ee4f0Sreyk errx(1, "passphrase does not match!"); 756cfe372e4Sreyk } 757901ee4f0Sreyk 758c03e9c27Stobhe len = snprintf(cacrt, sizeof(cacrt), "%s/ca.crt", ca->sslpath); 759c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(cacrt)) 760c03e9c27Stobhe err(1, "%s: snprintf", __func__); 761c03e9c27Stobhe len = snprintf(capfx, sizeof(capfx), "%s/ca.pfx", ca->sslpath); 762c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(capfx)) 763c03e9c27Stobhe err(1, "%s: snprintf", __func__); 764c03e9c27Stobhe len = snprintf(key, sizeof(key), "%s/private/%s.key", ca->sslpath, keyname); 765c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(key)) 766c03e9c27Stobhe err(1, "%s: snprintf", __func__); 767c03e9c27Stobhe len = snprintf(crt, sizeof(crt), "%s/%s.crt", ca->sslpath, keyname); 768c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(crt)) 769c03e9c27Stobhe err(1, "%s: snprintf", __func__); 770c03e9c27Stobhe len = snprintf(pfx, sizeof(pfx), "%s/private/%s.pfx", ca->sslpath, oname); 771c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(pfx)) 772c03e9c27Stobhe err(1, "%s: snprintf", __func__); 773731c78a3Stedu 774c03e9c27Stobhe len = snprintf(passenv, sizeof(passenv), "EXPASS=%s", pass); 775c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(passenv)) 776c03e9c27Stobhe err(1, "%s: snprintf", __func__); 777731c78a3Stedu putenv(passenv); 778731c78a3Stedu 7790dd4c7c3Sjsg if (keyname != NULL) { 780731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "pkcs12", "-export", 781731c78a3Stedu "-name", keyname, "-CAfile", cacrt, "-inkey", key, 782731c78a3Stedu "-in", crt, "-out", pfx, "-passout", "env:EXPASS", 783731c78a3Stedu "-passin", ca->passfile, NULL }; 784731c78a3Stedu ca_execv(cmd); 7850dd4c7c3Sjsg } 786901ee4f0Sreyk 787731c78a3Stedu char *pkcscmd[] = { PATH_OPENSSL, "pkcs12", "-export", 788731c78a3Stedu "-caname", ca->caname, "-name", ca->caname, "-cacerts", 789731c78a3Stedu "-nokeys", "-in", cacrt, "-out", capfx, 790731c78a3Stedu "-passout", "env:EXPASS", "-passin", ca->passfile, NULL }; 791731c78a3Stedu ca_execv(pkcscmd); 792731c78a3Stedu 793731c78a3Stedu unsetenv("EXPASS"); 794731c78a3Stedu explicit_bzero(passenv, sizeof(passenv)); 795901ee4f0Sreyk 796901ee4f0Sreyk if ((p = mkdtemp(tpl)) == NULL) 797901ee4f0Sreyk err(1, "could not create temp dir"); 798901ee4f0Sreyk 799a718290cSphessler chmod(p, 0755); 800a718290cSphessler 8012746af23Sjsg for (i = 0; i < nitems(hier); i++) { 802901ee4f0Sreyk strlcpy(dst, p, sizeof(dst)); 8032746af23Sjsg strlcat(dst, hier[i].dir, sizeof(dst)); 804465ad9d7Sjsg if (stat(dst, &st) != 0 && errno == ENOENT && 805465ad9d7Sjsg mkdir(dst, hier[i].mode) != 0) 806901ee4f0Sreyk err(1, "failed to create dir %s", dst); 807901ee4f0Sreyk } 808901ee4f0Sreyk 8090dd4c7c3Sjsg /* create a file with the address of the peer to connect to */ 8100dd4c7c3Sjsg if (myname != NULL) { 811c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/export/peer.txt", p); 812c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 813c03e9c27Stobhe err(1, "%s: snprintf", __func__); 8140dd4c7c3Sjsg if ((fd = open(dst, O_WRONLY|O_CREAT, 0644)) == -1) 8150dd4c7c3Sjsg err(1, "open %s", dst); 8162a5bbc1bStobhe if (write(fd, myname, strlen(myname)) == -1) 8172a5bbc1bStobhe err(1, "%s: write", __func__); 8180dd4c7c3Sjsg close(fd); 8190dd4c7c3Sjsg } 820901ee4f0Sreyk 821c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/ca.pfx", ca->sslpath); 822c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 823c03e9c27Stobhe err(1, "%s: snprintf", __func__); 824c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/export/ca.pfx", p); 825c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 826c03e9c27Stobhe err(1, "%s: snprintf", __func__); 8273da6623bSjsg fcopy(src, dst, 0644); 828901ee4f0Sreyk 829c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/ca.crt", ca->sslpath); 830c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 831c03e9c27Stobhe err(1, "%s: snprintf", __func__); 832c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/ca/ca.crt", p); 833c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 834c03e9c27Stobhe err(1, "%s: snprintf", __func__); 8353da6623bSjsg fcopy(src, dst, 0644); 836901ee4f0Sreyk 837c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/ca.crl", ca->sslpath); 838c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 839c03e9c27Stobhe err(1, "%s: snprintf", __func__); 8400dd4c7c3Sjsg if (stat(src, &st) == 0) { 841c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/crls/ca.crl", p); 842c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 843c03e9c27Stobhe err(1, "%s: snprintf", __func__); 8440dd4c7c3Sjsg fcopy(src, dst, 0644); 8450dd4c7c3Sjsg } 8460dd4c7c3Sjsg 8470dd4c7c3Sjsg if (keyname != NULL) { 848c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/private/%s.pfx", 849c03e9c27Stobhe ca->sslpath, oname); 850c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 851c03e9c27Stobhe err(1, "%s: snprintf", __func__); 852c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/export/%s.pfx", p, oname); 853c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 854c03e9c27Stobhe err(1, "%s: snprintf", __func__); 8550dd4c7c3Sjsg fcopy(src, dst, 0644); 8560dd4c7c3Sjsg 857c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/private/%s.key", 858c03e9c27Stobhe ca->sslpath, keyname); 859c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 860c03e9c27Stobhe err(1, "%s: snprintf", __func__); 861c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/private/%s.key", p, keyname); 862c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 863c03e9c27Stobhe err(1, "%s: snprintf", __func__); 864c03e9c27Stobhe fcopy(src, dst, 0600); 865c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/private/local.key", p); 866c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 867c03e9c27Stobhe err(1, "%s: snprintf", __func__); 868c03e9c27Stobhe fcopy(src, dst, 0600); 869c03e9c27Stobhe 870c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/%s.crt", ca->sslpath, 8710dd4c7c3Sjsg keyname); 872c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 873c03e9c27Stobhe err(1, "%s: snprintf", __func__); 874c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/certs/%s.crt", p, keyname); 875c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 876c03e9c27Stobhe err(1, "%s: snprintf", __func__); 8773da6623bSjsg fcopy(src, dst, 0644); 878901ee4f0Sreyk 879c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/local.pub", p); 880c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 881c03e9c27Stobhe err(1, "%s: snprintf", __func__); 882731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "rsa", "-out", dst, "-in", key, 883731c78a3Stedu "-pubout", NULL }; 884731c78a3Stedu ca_execv(cmd); 8850dd4c7c3Sjsg } 886901ee4f0Sreyk 887901ee4f0Sreyk if (stat(PATH_TAR, &st) == 0) { 888c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s.tgz", oname); 889c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 890c03e9c27Stobhe err(1, "%s: snprintf", __func__); 891731c78a3Stedu if (keyname == NULL) { 892731c78a3Stedu char *cmd[] = { PATH_TAR, "-zcf", src, 893731c78a3Stedu "-C", ca->sslpath, ".", NULL }; 894731c78a3Stedu ca_execv(cmd); 895731c78a3Stedu } else { 896731c78a3Stedu char *cmd[] = { PATH_TAR, "-zcf", src, "-C", p, ".", 897731c78a3Stedu NULL }; 898731c78a3Stedu ca_execv(cmd); 899731c78a3Stedu } 900901ee4f0Sreyk if (realpath(src, dst) != NULL) 901901ee4f0Sreyk printf("exported files in %s\n", dst); 902901ee4f0Sreyk } 903901ee4f0Sreyk 904901ee4f0Sreyk if (stat(PATH_ZIP, &st) == 0) { 9050dd4c7c3Sjsg dexp = opendir(EXPDIR); 9060dd4c7c3Sjsg if (dexp) { 9070dd4c7c3Sjsg while ((de = readdir(dexp)) != NULL) { 9080dd4c7c3Sjsg if (!strcmp(de->d_name, ".") || 9090dd4c7c3Sjsg !strcmp(de->d_name, "..")) 9100dd4c7c3Sjsg continue; 911c03e9c27Stobhe len = snprintf(src, sizeof(src), "%s/%s", 912c03e9c27Stobhe EXPDIR, de->d_name); 913c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(src)) 914c03e9c27Stobhe err(1, "%s: snprintf", __func__); 915c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/export/%s", 916c03e9c27Stobhe p, de->d_name); 917c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 918c03e9c27Stobhe err(1, "%s: snprintf", __func__); 919d513b0f4Ssemarie fcopy(src, dst, 0644); 9200dd4c7c3Sjsg } 9210dd4c7c3Sjsg closedir(dexp); 9220dd4c7c3Sjsg } 9230dd4c7c3Sjsg 924c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/export", p); 925c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 926c03e9c27Stobhe err(1, "%s: snprintf", __func__); 927901ee4f0Sreyk if (getcwd(src, sizeof(src)) == NULL) 928901ee4f0Sreyk err(1, "could not get cwd"); 929901ee4f0Sreyk 930901ee4f0Sreyk if (chdir(dst) == -1) 931901ee4f0Sreyk err(1, "could not change %s", dst); 932901ee4f0Sreyk 933c03e9c27Stobhe len = snprintf(dst, sizeof(dst), "%s/%s.zip", src, oname); 934c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(dst)) 935c03e9c27Stobhe err(1, "%s: snprintf", __func__); 936731c78a3Stedu char *cmd[] = { PATH_ZIP, "-qr", dst, ".", NULL }; 937731c78a3Stedu ca_execv(cmd); 938901ee4f0Sreyk printf("exported files in %s\n", dst); 939901ee4f0Sreyk 940901ee4f0Sreyk if (chdir(src) == -1) 941901ee4f0Sreyk err(1, "could not change %s", dst); 942901ee4f0Sreyk } 943901ee4f0Sreyk 944901ee4f0Sreyk rm_dir(p); 945901ee4f0Sreyk 946901ee4f0Sreyk return (0); 947901ee4f0Sreyk } 948901ee4f0Sreyk 9498e3cf88fSjsg /* create index if it doesn't already exist */ 9508e3cf88fSjsg void 9518e3cf88fSjsg ca_create_index(struct ca *ca) 9528e3cf88fSjsg { 9538e3cf88fSjsg struct stat st; 9548e3cf88fSjsg int fd; 955c03e9c27Stobhe int len; 9568e3cf88fSjsg 957c03e9c27Stobhe len = snprintf(ca->index, sizeof(ca->index), "%s/index.txt", 958c03e9c27Stobhe ca->sslpath); 959c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(ca->index)) 960c03e9c27Stobhe err(1, "%s: snprintf", __func__); 9618e3cf88fSjsg if (stat(ca->index, &st) != 0) { 9628e3cf88fSjsg if (errno == ENOENT) { 9638e3cf88fSjsg if ((fd = open(ca->index, O_WRONLY | O_CREAT, 0644)) 9648e3cf88fSjsg == -1) 9658e3cf88fSjsg err(1, "could not create file %s", ca->index); 9668e3cf88fSjsg close(fd); 9678e3cf88fSjsg } else 9688e3cf88fSjsg err(1, "could not access %s", ca->index); 9698e3cf88fSjsg } 9708e3cf88fSjsg 971c03e9c27Stobhe len = snprintf(ca->serial, sizeof(ca->serial), "%s/serial.txt", 972c03e9c27Stobhe ca->sslpath); 973c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(ca->serial)) 974c03e9c27Stobhe err(1, "%s: snprintf", __func__); 9758e3cf88fSjsg if (stat(ca->serial, &st) != 0) { 9768e3cf88fSjsg if (errno == ENOENT) { 9778e3cf88fSjsg if ((fd = open(ca->serial, O_WRONLY | O_CREAT, 0644)) 9788e3cf88fSjsg == -1) 9798e3cf88fSjsg err(1, "could not create file %s", ca->serial); 9808e3cf88fSjsg /* serial file must be created with a number */ 9818e3cf88fSjsg if (write(fd, "01\n", 3) != 3) 9828e3cf88fSjsg err(1, "write %s", ca->serial); 9838e3cf88fSjsg close(fd); 9848e3cf88fSjsg } else 9858e3cf88fSjsg err(1, "could not access %s", ca->serial); 9868e3cf88fSjsg } 9878e3cf88fSjsg } 9888e3cf88fSjsg 989c3cc2c5eSjsg int 990c3cc2c5eSjsg ca_revoke(struct ca *ca, char *keyname) 991c3cc2c5eSjsg { 992c3cc2c5eSjsg struct stat st; 993c3cc2c5eSjsg char path[PATH_MAX]; 994731c78a3Stedu char cakey[PATH_MAX]; 995731c78a3Stedu char cacrt[PATH_MAX]; 996c03e9c27Stobhe size_t len; 997c3cc2c5eSjsg 9987430271cSphessler if (keyname) { 999c03e9c27Stobhe len = snprintf(path, sizeof(path), "%s/%s.crt", 1000c3cc2c5eSjsg ca->sslpath, keyname); 1001c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(path)) 1002c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1003c3cc2c5eSjsg if (stat(path, &st) != 0) { 1004c3cc2c5eSjsg warn("Problem with certificate for '%s'", keyname); 1005c3cc2c5eSjsg return (1); 1006c3cc2c5eSjsg } 10077430271cSphessler } 1008c3cc2c5eSjsg 10098e3cf88fSjsg ca_create_index(ca); 1010c3cc2c5eSjsg 10118e3cf88fSjsg ca_setenv("$ENV::CADB", ca->index); 10128e3cf88fSjsg ca_setenv("$ENV::CASERIAL", ca->serial); 101355ace1c0Sjsg if (keyname) 101455ace1c0Sjsg ca_setenv("$ENV::REQ_EXT", ""); 101555ace1c0Sjsg 10164f23bdabSreyk ca_setcnf(ca, "ca-revoke"); 10174f23bdabSreyk 1018c03e9c27Stobhe len = snprintf(cakey, sizeof(cakey), "%s/private/ca.key", ca->sslpath); 1019c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(cakey)) 1020c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1021c03e9c27Stobhe len = snprintf(cacrt, sizeof(cacrt), "%s/ca.crt", ca->sslpath); 1022c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(cacrt)) 1023c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1024731c78a3Stedu 10257430271cSphessler if (keyname) { 1026731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "ca", "-config", ca->sslcnf, 1027731c78a3Stedu "-keyfile", cakey, "-passin", ca->passfile, "-cert", cacrt, 1028731c78a3Stedu "-revoke", path, ca->batch, NULL }; 1029731c78a3Stedu ca_execv(cmd); 10307430271cSphessler } 1031c3cc2c5eSjsg 1032c03e9c27Stobhe len = snprintf(path, sizeof(path), "%s/ca.crl", ca->sslpath); 1033c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(path)) 1034c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1035731c78a3Stedu char *cmd[] = { PATH_OPENSSL, "ca", "-config", ca->sslcnf, 1036731c78a3Stedu "-keyfile", cakey, "-passin", ca->passfile, "-gencrl", 1037*7c9b6f9dSpascal "-cert", cacrt, "-out", path, ca->batch, NULL }; 1038731c78a3Stedu ca_execv(cmd); 1039c3cc2c5eSjsg 1040c3cc2c5eSjsg return (0); 1041c3cc2c5eSjsg } 1042c3cc2c5eSjsg 10434f23bdabSreyk void 10444f23bdabSreyk ca_clrenv(void) 10454f23bdabSreyk { 10464f23bdabSreyk int i; 10478068c079Spatrick for (i = 0; ca_env[i][0] != NULL; i++) { 10488068c079Spatrick free(ca_env[i][1]); 10494f23bdabSreyk ca_env[i][1] = NULL; 10504f23bdabSreyk } 10518068c079Spatrick } 10524f23bdabSreyk 10534f23bdabSreyk void 10544f23bdabSreyk ca_setenv(const char *key, const char *value) 10554f23bdabSreyk { 10564f23bdabSreyk int i; 10578068c079Spatrick char *p = NULL; 10584f23bdabSreyk 10594f23bdabSreyk for (i = 0; ca_env[i][0] != NULL; i++) { 10604f23bdabSreyk if (strcmp(ca_env[i][0], key) == 0) { 10614f23bdabSreyk if (ca_env[i][1] != NULL) 10624f23bdabSreyk errx(1, "env %s already set: %s", key, value); 10638068c079Spatrick p = strdup(value); 10648068c079Spatrick if (p == NULL) 10658068c079Spatrick err(1, NULL); 10668068c079Spatrick ca_env[i][1] = p; 10674f23bdabSreyk return; 10684f23bdabSreyk } 10694f23bdabSreyk } 10704f23bdabSreyk errx(1, "env %s invalid", key); 10714f23bdabSreyk } 10724f23bdabSreyk 10734f23bdabSreyk void 10744f23bdabSreyk ca_setcnf(struct ca *ca, const char *keyname) 10754f23bdabSreyk { 10764f23bdabSreyk struct stat st; 10774f23bdabSreyk const char *extcnf, *sslcnf; 1078c03e9c27Stobhe int len; 10794f23bdabSreyk 10804f23bdabSreyk if (stat(IKECA_CNF, &st) == 0) { 10814f23bdabSreyk extcnf = IKECA_CNF; 10824f23bdabSreyk sslcnf = IKECA_CNF; 10834f23bdabSreyk } else { 10844f23bdabSreyk extcnf = X509_CNF; 10854f23bdabSreyk sslcnf = SSL_CNF; 10864f23bdabSreyk } 10874f23bdabSreyk 1088c03e9c27Stobhe len = snprintf(ca->extcnf, sizeof(ca->extcnf), "%s/%s-ext.cnf", 10894f23bdabSreyk ca->sslpath, keyname); 1090c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(ca->extcnf)) 1091c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1092c03e9c27Stobhe len = snprintf(ca->sslcnf, sizeof(ca->sslcnf), "%s/%s-ssl.cnf", 10934f23bdabSreyk ca->sslpath, keyname); 1094c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(ca->sslcnf)) 1095c03e9c27Stobhe err(1, "%s: snprintf", __func__); 10964f23bdabSreyk 10974f23bdabSreyk fcopy_env(extcnf, ca->extcnf, 0400); 10984f23bdabSreyk fcopy_env(sslcnf, ca->sslcnf, 0400); 10994f23bdabSreyk } 11004f23bdabSreyk 1101901ee4f0Sreyk struct ca * 1102cfd26ebdSreyk ca_setup(char *caname, int create, int quiet, char *pass) 1103901ee4f0Sreyk { 1104901ee4f0Sreyk struct stat st; 1105901ee4f0Sreyk struct ca *ca; 1106901ee4f0Sreyk char path[PATH_MAX]; 1107c03e9c27Stobhe int len; 1108901ee4f0Sreyk 1109901ee4f0Sreyk if (stat(PATH_OPENSSL, &st) == -1) 1110901ee4f0Sreyk err(1, "openssl binary not available"); 1111901ee4f0Sreyk 1112901ee4f0Sreyk if ((ca = calloc(1, sizeof(struct ca))) == NULL) 1113901ee4f0Sreyk err(1, "calloc"); 1114901ee4f0Sreyk 1115901ee4f0Sreyk ca->caname = strdup(caname); 1116c03e9c27Stobhe len = snprintf(ca->sslpath, sizeof(ca->sslpath), SSLDIR "/%s", caname); 1117c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(ca->sslpath)) 1118c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1119901ee4f0Sreyk 1120cfd26ebdSreyk if (quiet) 1121731c78a3Stedu ca->batch = "-batch"; 1122cfd26ebdSreyk 1123901ee4f0Sreyk if (create == 0 && stat(ca->sslpath, &st) == -1) { 1124901ee4f0Sreyk free(ca->caname); 1125901ee4f0Sreyk free(ca); 1126901ee4f0Sreyk errx(1, "CA '%s' does not exist", caname); 1127901ee4f0Sreyk } 1128901ee4f0Sreyk 1129901ee4f0Sreyk strlcpy(path, ca->sslpath, sizeof(path)); 1130901ee4f0Sreyk if (mkdir(path, 0777) == -1 && errno != EEXIST) 1131901ee4f0Sreyk err(1, "failed to create dir %s", path); 1132901ee4f0Sreyk strlcat(path, "/private", sizeof(path)); 1133901ee4f0Sreyk if (mkdir(path, 0700) == -1 && errno != EEXIST) 1134901ee4f0Sreyk err(1, "failed to create dir %s", path); 1135901ee4f0Sreyk 1136c03e9c27Stobhe len = snprintf(path, sizeof(path), "%s/ikeca.passwd", ca->sslpath); 1137c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(path)) 1138c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1139731c78a3Stedu if (create && stat(path, &st) == -1 && errno == ENOENT) 1140731c78a3Stedu ca_newpass(path, pass); 1141c03e9c27Stobhe len = snprintf(ca->passfile, sizeof(ca->passfile), "file:%s", path); 1142c03e9c27Stobhe if (len < 0 || (size_t)len >= sizeof(ca->passfile)) 1143c03e9c27Stobhe err(1, "%s: snprintf", __func__); 1144901ee4f0Sreyk 1145901ee4f0Sreyk return (ca); 1146901ee4f0Sreyk } 1147731c78a3Stedu 1148731c78a3Stedu int static 1149731c78a3Stedu ca_execv(char *const argv[]) 1150731c78a3Stedu { 1151731c78a3Stedu pid_t pid, cpid; 1152731c78a3Stedu int status; 1153731c78a3Stedu 1154731c78a3Stedu switch (cpid = fork()) { 1155731c78a3Stedu case -1: 1156731c78a3Stedu return -1; 1157731c78a3Stedu case 0: 1158731c78a3Stedu execv(argv[0], argv); 1159731c78a3Stedu _exit(127); 1160731c78a3Stedu } 1161731c78a3Stedu 1162731c78a3Stedu do { 1163731c78a3Stedu pid = waitpid(cpid, &status, 0); 1164731c78a3Stedu } while (pid == -1 && errno == EINTR); 1165731c78a3Stedu 1166731c78a3Stedu return (pid == -1 ? -1 : WEXITSTATUS(status)); 1167731c78a3Stedu } 1168