src/usr.sbin/ikectl/ikeca.c

1168 lines
29 KiB
C

/* $OpenBSD: ikeca.c,v 1.51 2021/01/23 22:04:55 tobhe Exp $ */
/*
* Copyright (c) 2010 Jonathan Gray <jsg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <pwd.h>
#include <fcntl.h>
#include <fts.h>
#include <dirent.h>
#include <limits.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include "types.h"
#include "parser.h"
#ifndef PREFIX
#define PREFIX ""
#endif
#ifndef SSLDIR
#define SSLDIR PREFIX "/etc/ssl"
#endif
#define SSL_CNF SSLDIR "/openssl.cnf"
#define X509_CNF SSLDIR "/x509v3.cnf"
#define IKECA_CNF SSLDIR "/ikeca.cnf"
#define KEYBASE PREFIX "/etc/iked"
#ifndef EXPDIR
#define EXPDIR PREFIX "/usr/share/iked"
#endif
#ifndef PATH_OPENSSL
#define PATH_OPENSSL "/usr/bin/openssl"
#endif
#ifndef PATH_ZIP
#define PATH_ZIP "/usr/local/bin/zip"
#endif
#ifndef PATH_TAR
#define PATH_TAR "/bin/tar"
#endif
struct ca {
char sslpath[PATH_MAX];
char passfile[PATH_MAX + 5]; /* Includes the "file:" prefix */
char index[PATH_MAX];
char serial[PATH_MAX];
char sslcnf[PATH_MAX];
char extcnf[PATH_MAX];
char *batch;
char *caname;
};
struct {
char *dir;
mode_t mode;
} hier[] = {
{ "", 0755 },
{ "/ca", 0755 },
{ "/certs", 0755 },
{ "/crls", 0755 },
{ "/export", 0755 },
{ "/private", 0700 }
};
/* explicitly list allowed variables */
char *ca_env[][2] = {
{ "$ENV::CADB", NULL },
{ "$ENV::CASERIAL", NULL },
{ "$ENV::CERTFQDN", NULL },
{ "$ENV::CERTIP", NULL },
{ "$ENV::CERTPATHLEN", NULL },
{ "$ENV::CERTUSAGE", NULL },
{ "$ENV::CERT_C", NULL },
{ "$ENV::CERT_CN", NULL },
{ "$ENV::CERT_EMAIL", NULL },
{ "$ENV::CERT_L", NULL },
{ "$ENV::CERT_O", NULL },
{ "$ENV::CERT_OU", NULL },
{ "$ENV::CERT_ST", NULL },
{ "$ENV::EXTCERTUSAGE", NULL },
{ "$ENV::NSCERTTYPE", NULL },
{ "$ENV::REQ_EXT", NULL },
{ NULL }
};
int ca_sign(struct ca *, char *, int);
int ca_request(struct ca *, char *, int);
void ca_newpass(char *, char *);
int fcopy(char *, char *, mode_t);
void fcopy_env(const char *, const char *, mode_t);
int rm_dir(char *);
void ca_hier(char *);
void ca_setenv(const char *, const char *);
void ca_clrenv(void);
void ca_setcnf(struct ca *, const char *);
void ca_create_index(struct ca *);
int static ca_execv(char *const []);
/* util.c */
int expand_string(char *, size_t, const char *, const char *);
int
ca_delete(struct ca *ca)
{
return (rm_dir(ca->sslpath));
}
int
ca_key_create(struct ca *ca, char *keyname)
{
struct stat st;
char path[PATH_MAX];
int len;
len = snprintf(path, sizeof(path), "%s/private/%s.key",
ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(path))
err(1, "%s: snprintf", __func__);
/* don't recreate key if one is already present */
if (stat(path, &st) == 0) {
return (0);
}
char *cmd[] = { PATH_OPENSSL, "genrsa", "-out", path, "2048", NULL };
ca_execv(cmd);
chmod(path, 0600);
return (0);
}
int
ca_key_import(struct ca *ca, char *keyname, char *import)
{
struct stat st;
char dst[PATH_MAX];
int len;
if (stat(import, &st) != 0) {
warn("could not access keyfile %s", import);
return (1);
}
len = snprintf(dst, sizeof(dst), "%s/private/%s.key", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(import, dst, 0600);
return (0);
}
int
ca_key_delete(struct ca *ca, char *keyname)
{
char path[PATH_MAX];
int len;
len = snprintf(path, sizeof(path), "%s/private/%s.key",
ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(path))
err(1, "%s: snprintf", __func__);
unlink(path);
return (0);
}
int
ca_delkey(struct ca *ca, char *keyname)
{
char file[PATH_MAX];
int len;
len = snprintf(file, sizeof(file), "%s/%s.crt", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(file))
err(1, "%s: snprintf", __func__);
unlink(file);
len = snprintf(file, sizeof(file), "%s/private/%s.key", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(file))
err(1, "%s: snprintf", __func__);
unlink(file);
len = snprintf(file, sizeof(file), "%s/private/%s.csr", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(file))
err(1, "%s: snprintf", __func__);
unlink(file);
len = snprintf(file, sizeof(file), "%s/private/%s.pfx", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(file))
err(1, "%s: snprintf", __func__);
unlink(file);
return (0);
}
int
ca_request(struct ca *ca, char *keyname, int type)
{
char hostname[HOST_NAME_MAX+1];
char name[128];
char key[PATH_MAX];
char path[PATH_MAX];
int len;
ca_setenv("$ENV::CERT_CN", keyname);
strlcpy(name, keyname, sizeof(name));
if (type == HOST_IPADDR) {
ca_setenv("$ENV::CERTIP", name);
ca_setenv("$ENV::REQ_EXT", "x509v3_IPAddr");
} else if (type == HOST_FQDN) {
if (!strcmp(keyname, "local")) {
if (gethostname(hostname, sizeof(hostname)))
err(1, "gethostname");
strlcpy(name, hostname, sizeof(name));
}
ca_setenv("$ENV::CERTFQDN", name);
ca_setenv("$ENV::REQ_EXT", "x509v3_FQDN");
} else {
errx(1, "unknown host type %d", type);
}
ca_setcnf(ca, keyname);
len = snprintf(key, sizeof(key), "%s/private/%s.key", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(key))
err(1, "%s: snprintf", __func__);
len = snprintf(path, sizeof(path), "%s/private/%s.csr", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(path))
err(1, "%s: snprintf", __func__);
char *cmd[] = { PATH_OPENSSL, "req", "-new", "-key", key, "-out", path,
"-config", ca->sslcnf, ca->batch, NULL };
ca_execv(cmd);
chmod(path, 0600);
return (0);
}
int
ca_sign(struct ca *ca, char *keyname, int type)
{
char cakey[PATH_MAX];
char cacrt[PATH_MAX];
char out[PATH_MAX];
char in[PATH_MAX];
char *extensions = NULL;
int len;
if (type == HOST_IPADDR) {
extensions = "x509v3_IPAddr";
} else if (type == HOST_FQDN) {
extensions = "x509v3_FQDN";
} else {
errx(1, "unknown host type %d", type);
}
ca_create_index(ca);
ca_setenv("$ENV::CADB", ca->index);
ca_setenv("$ENV::CASERIAL", ca->serial);
ca_setcnf(ca, keyname);
len = snprintf(cakey, sizeof(cakey), "%s/private/ca.key", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(cakey))
err(1, "%s: snprintf", __func__);
len = snprintf(cacrt, sizeof(cacrt), "%s/ca.crt", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(cacrt))
err(1, "%s: snprintf", __func__);
len = snprintf(out, sizeof(out), "%s/%s.crt", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(out))
err(1, "%s: snprintf", __func__);
len = snprintf(in, sizeof(in), "%s/private/%s.csr", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(in))
err(1, "%s: snprintf", __func__);
char *cmd[] = { PATH_OPENSSL, "ca", "-config", ca->sslcnf,
"-keyfile", cakey, "-cert", cacrt, "-extfile", ca->extcnf,
"-extensions", extensions, "-out", out, "-in", in,
"-passin", ca->passfile, "-outdir", ca->sslpath, "-batch", NULL };
ca_execv(cmd);
return (0);
}
int
ca_certificate(struct ca *ca, char *keyname, int type, int action)
{
ca_clrenv();
switch (action) {
case CA_SERVER:
ca_setenv("$ENV::EXTCERTUSAGE", "serverAuth");
ca_setenv("$ENV::NSCERTTYPE", "server");
ca_setenv("$ENV::CERTUSAGE",
"digitalSignature,keyEncipherment");
break;
case CA_CLIENT:
ca_setenv("$ENV::EXTCERTUSAGE", "clientAuth");
ca_setenv("$ENV::NSCERTTYPE", "client");
ca_setenv("$ENV::CERTUSAGE",
"digitalSignature,keyAgreement");
break;
case CA_OCSP:
ca_setenv("$ENV::EXTCERTUSAGE", "OCSPSigning");
ca_setenv("$ENV::CERTUSAGE",
"nonRepudiation,digitalSignature,keyEncipherment");
break;
default:
break;
}
ca_key_create(ca, keyname);
ca_request(ca, keyname, type);
ca_sign(ca, keyname, type);
return (0);
}
int
ca_key_install(struct ca *ca, char *keyname, char *dir)
{
struct stat st;
char src[PATH_MAX];
char dst[PATH_MAX];
char out[PATH_MAX];
char *p = NULL;
int len;
len = snprintf(src, sizeof(src), "%s/private/%s.key", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
if (stat(src, &st) == -1) {
if (errno == ENOENT)
printf("key for '%s' does not exist\n", ca->caname);
else
warn("could not access key");
return (1);
}
if (dir == NULL)
p = dir = strdup(KEYBASE);
ca_hier(dir);
len = snprintf(dst, sizeof(dst), "%s/private/local.key", dir);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0600);
len = snprintf(out, sizeof(out), "%s/local.pub", dir);
if (len < 0 || (size_t)len >= sizeof(out))
err(1, "%s: snprintf", __func__);
char *cmd[] = { PATH_OPENSSL, "rsa", "-out", out, "-in", dst,
"-pubout", NULL };
ca_execv(cmd);
free(p);
return (0);
}
int
ca_cert_install(struct ca *ca, char *keyname, char *dir)
{
char src[PATH_MAX];
char dst[PATH_MAX];
int r;
char *p = NULL;
int len;
if (dir == NULL)
p = dir = strdup(KEYBASE);
ca_hier(dir);
if ((r = ca_key_install(ca, keyname, dir)) != 0) {
free(dir);
return (r);
}
len = snprintf(src, sizeof(src), "%s/%s.crt", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
len = snprintf(dst, sizeof(dst), "%s/certs/%s.crt", dir, keyname);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0644);
free(p);
return (0);
}
void
ca_newpass(char *passfile, char *password)
{
FILE *f;
char *pass;
char prev[_PASSWORD_LEN + 1];
if (password != NULL) {
pass = password;
goto done;
}
pass = getpass("CA passphrase:");
if (pass == NULL || *pass == '\0')
err(1, "password not set");
strlcpy(prev, pass, sizeof(prev));
pass = getpass("Retype CA passphrase:");
if (pass == NULL || strcmp(prev, pass) != 0)
errx(1, "passphrase does not match!");
done:
if ((f = fopen(passfile, "wb")) == NULL)
err(1, "could not open passfile %s", passfile);
chmod(passfile, 0600);
fprintf(f, "%s\n%s\n", pass, pass);
fclose(f);
}
int
ca_create(struct ca *ca)
{
char key[PATH_MAX];
char csr[PATH_MAX];
char crt[PATH_MAX];
int len;
ca_clrenv();
len = snprintf(key, sizeof(key), "%s/private/ca.key", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(key))
err(1, "%s: snprintf", __func__);
char *genrsa[] = { PATH_OPENSSL, "genrsa", "-aes256", "-out", key,
"-passout", ca->passfile, "2048", NULL };
ca_execv(genrsa);
chmod(key, 0600);
ca_setenv("$ENV::CERT_CN", "VPN CA");
ca_setenv("$ENV::REQ_EXT", "x509v3_CA");
ca_setcnf(ca, "ca");
len = snprintf(csr, sizeof(csr), "%s/private/ca.csr", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(csr))
err(1, "%s: snprintf", __func__);
char *reqcmd[] = { PATH_OPENSSL, "req", "-new", "-key", key,
"-config", ca->sslcnf, "-out", csr,
"-passin", ca->passfile, ca->batch, NULL };
ca_execv(reqcmd);
chmod(csr, 0600);
len = snprintf(crt, sizeof(crt), "%s/ca.crt", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(crt))
err(1, "%s: snprintf", __func__);
char *x509[] = { PATH_OPENSSL, "x509", "-req", "-days", "4500",
"-in", csr, "-signkey", key, "-sha256",
"-extfile", ca->extcnf, "-extensions", "x509v3_CA",
"-out", crt, "-passin", ca->passfile, NULL };
ca_execv(x509);
/* Create the CRL revocation list */
ca_revoke(ca, NULL);
return (0);
}
int
ca_install(struct ca *ca, char *dir)
{
struct stat st;
char src[PATH_MAX];
char dst[PATH_MAX];
char *p = NULL;
int len;
len = snprintf(src, sizeof(src), "%s/ca.crt", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
if (stat(src, &st) == -1) {
printf("CA '%s' does not exist\n", ca->caname);
return (1);
}
if (dir == NULL)
p = dir = strdup(KEYBASE);
ca_hier(dir);
len = snprintf(dst, sizeof(dst), "%s/ca/ca.crt", dir);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
if (fcopy(src, dst, 0644) == 0)
printf("certificate for CA '%s' installed into %s\n",
ca->caname, dst);
len = snprintf(src, sizeof(src), "%s/ca.crl", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
if (stat(src, &st) == 0) {
len = snprintf(dst, sizeof(dst), "%s/crls/ca.crl", dir);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
if (fcopy(src, dst, 0644) == 0)
printf("CRL for CA '%s' installed to %s\n",
ca->caname, dst);
}
free(p);
return (0);
}
int
ca_show_certs(struct ca *ca, char *name)
{
DIR *dir;
struct dirent *de;
char path[PATH_MAX];
char *p;
struct stat st;
int len;
if (name != NULL) {
len = snprintf(path, sizeof(path), "%s/%s.crt",
ca->sslpath, name);
if (len < 0 || (size_t)len >= sizeof(path))
err(1, "%s: snprintf", __func__);
if (stat(path, &st) != 0)
err(1, "could not open file %s.crt", name);
char *cmd[] = { PATH_OPENSSL, "x509", "-text",
"-in", path, NULL };
ca_execv(cmd);
printf("\n");
return (0);
}
if ((dir = opendir(ca->sslpath)) == NULL)
err(1, "could not open directory %s", ca->sslpath);
while ((de = readdir(dir)) != NULL) {
if (de->d_namlen > 4) {
p = de->d_name + de->d_namlen - 4;
if (strcmp(".crt", p) != 0)
continue;
len = snprintf(path, sizeof(path), "%s/%s", ca->sslpath,
de->d_name);
if (len < 0 || (size_t)len >= sizeof(path))
err(1, "%s: snprintf", __func__);
char *cmd[] = { PATH_OPENSSL, "x509", "-subject",
"-fingerprint", "-dates", "-noout", "-in", path,
NULL };
ca_execv(cmd);
printf("\n");
}
}
closedir(dir);
return (0);
}
int
fcopy(char *src, char *dst, mode_t mode)
{
int ifd, ofd;
uint8_t buf[BUFSIZ];
ssize_t r;
if ((ifd = open(src, O_RDONLY)) == -1)
err(1, "open %s", src);
if ((ofd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
int saved_errno = errno;
close(ifd);
errc(1, saved_errno, "open %s", dst);
}
while ((r = read(ifd, buf, sizeof(buf))) > 0) {
if (write(ofd, buf, r) == -1)
err(1, "%s: write", __func__);
}
close(ofd);
close(ifd);
return (r == -1);
}
void
fcopy_env(const char *src, const char *dst, mode_t mode)
{
int ofd = -1, i;
uint8_t buf[BUFSIZ];
ssize_t r = -1, len;
FILE *ifp = NULL;
int saved_errno;
if ((ifp = fopen(src, "r")) == NULL)
err(1, "fopen %s", src);
if ((ofd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1)
goto done;
while (fgets(buf, sizeof(buf), ifp) != NULL) {
for (i = 0; ca_env[i][0] != NULL; i++) {
if (ca_env[i][1] == NULL)
continue;
if (expand_string(buf, sizeof(buf),
ca_env[i][0], ca_env[i][1]) == -1)
errx(1, "env %s value too long", ca_env[i][0]);
}
len = strlen(buf);
if (write(ofd, buf, len) != len)
goto done;
}
r = 0;
done:
saved_errno = errno;
close(ofd);
if (ifp != NULL)
fclose(ifp);
if (r == -1)
errc(1, saved_errno, "open %s", dst);
}
int
rm_dir(char *path)
{
FTS *fts;
FTSENT *p;
static char *fpath[] = { NULL, NULL };
fpath[0] = path;
if ((fts = fts_open(fpath, FTS_PHYSICAL, NULL)) == NULL) {
warn("fts_open %s", path);
return (1);
}
while ((p = fts_read(fts)) != NULL) {
switch (p->fts_info) {
case FTS_DP:
case FTS_DNR:
if (rmdir(p->fts_accpath) == -1)
warn("rmdir %s", p->fts_accpath);
break;
case FTS_F:
if (unlink(p->fts_accpath) == -1)
warn("unlink %s", p->fts_accpath);
break;
case FTS_D:
case FTS_DOT:
default:
continue;
}
}
fts_close(fts);
return (0);
}
void
ca_hier(char *path)
{
struct stat st;
char dst[PATH_MAX];
unsigned int i;
for (i = 0; i < nitems(hier); i++) {
strlcpy(dst, path, sizeof(dst));
strlcat(dst, hier[i].dir, sizeof(dst));
if (stat(dst, &st) != 0 && errno == ENOENT &&
mkdir(dst, hier[i].mode) != 0)
err(1, "failed to create dir %s", dst);
}
}
int
ca_export(struct ca *ca, char *keyname, char *myname, char *password)
{
DIR *dexp;
struct dirent *de;
struct stat st;
char *pass;
char prev[_PASSWORD_LEN + 1];
char passenv[_PASSWORD_LEN + 8];
char oname[PATH_MAX];
char src[PATH_MAX];
char dst[PATH_MAX];
char cacrt[PATH_MAX];
char capfx[PATH_MAX];
char key[PATH_MAX];
char crt[PATH_MAX];
char pfx[PATH_MAX];
char *p;
char tpl[] = "/tmp/ikectl.XXXXXXXXXX";
unsigned int i;
int fd;
int len;
if (keyname != NULL) {
if (strlcpy(oname, keyname, sizeof(oname)) >= sizeof(oname))
errx(1, "name too long");
} else {
strlcpy(oname, "ca", sizeof(oname));
}
/* colons are not valid characters in windows filenames... */
while ((p = strchr(oname, ':')) != NULL)
*p = '_';
if (password != NULL)
pass = password;
else {
pass = getpass("Export passphrase:");
if (pass == NULL || *pass == '\0')
err(1, "password not set");
strlcpy(prev, pass, sizeof(prev));
pass = getpass("Retype export passphrase:");
if (pass == NULL || strcmp(prev, pass) != 0)
errx(1, "passphrase does not match!");
}
len = snprintf(cacrt, sizeof(cacrt), "%s/ca.crt", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(cacrt))
err(1, "%s: snprintf", __func__);
len = snprintf(capfx, sizeof(capfx), "%s/ca.pfx", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(capfx))
err(1, "%s: snprintf", __func__);
len = snprintf(key, sizeof(key), "%s/private/%s.key", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(key))
err(1, "%s: snprintf", __func__);
len = snprintf(crt, sizeof(crt), "%s/%s.crt", ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(crt))
err(1, "%s: snprintf", __func__);
len = snprintf(pfx, sizeof(pfx), "%s/private/%s.pfx", ca->sslpath, oname);
if (len < 0 || (size_t)len >= sizeof(pfx))
err(1, "%s: snprintf", __func__);
len = snprintf(passenv, sizeof(passenv), "EXPASS=%s", pass);
if (len < 0 || (size_t)len >= sizeof(passenv))
err(1, "%s: snprintf", __func__);
putenv(passenv);
if (keyname != NULL) {
char *cmd[] = { PATH_OPENSSL, "pkcs12", "-export",
"-name", keyname, "-CAfile", cacrt, "-inkey", key,
"-in", crt, "-out", pfx, "-passout", "env:EXPASS",
"-passin", ca->passfile, NULL };
ca_execv(cmd);
}
char *pkcscmd[] = { PATH_OPENSSL, "pkcs12", "-export",
"-caname", ca->caname, "-name", ca->caname, "-cacerts",
"-nokeys", "-in", cacrt, "-out", capfx,
"-passout", "env:EXPASS", "-passin", ca->passfile, NULL };
ca_execv(pkcscmd);
unsetenv("EXPASS");
explicit_bzero(passenv, sizeof(passenv));
if ((p = mkdtemp(tpl)) == NULL)
err(1, "could not create temp dir");
chmod(p, 0755);
for (i = 0; i < nitems(hier); i++) {
strlcpy(dst, p, sizeof(dst));
strlcat(dst, hier[i].dir, sizeof(dst));
if (stat(dst, &st) != 0 && errno == ENOENT &&
mkdir(dst, hier[i].mode) != 0)
err(1, "failed to create dir %s", dst);
}
/* create a file with the address of the peer to connect to */
if (myname != NULL) {
len = snprintf(dst, sizeof(dst), "%s/export/peer.txt", p);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
if ((fd = open(dst, O_WRONLY|O_CREAT, 0644)) == -1)
err(1, "open %s", dst);
if (write(fd, myname, strlen(myname)) == -1)
err(1, "%s: write", __func__);
close(fd);
}
len = snprintf(src, sizeof(src), "%s/ca.pfx", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
len = snprintf(dst, sizeof(dst), "%s/export/ca.pfx", p);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0644);
len = snprintf(src, sizeof(src), "%s/ca.crt", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
len = snprintf(dst, sizeof(dst), "%s/ca/ca.crt", p);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0644);
len = snprintf(src, sizeof(src), "%s/ca.crl", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
if (stat(src, &st) == 0) {
len = snprintf(dst, sizeof(dst), "%s/crls/ca.crl", p);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0644);
}
if (keyname != NULL) {
len = snprintf(src, sizeof(src), "%s/private/%s.pfx",
ca->sslpath, oname);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
len = snprintf(dst, sizeof(dst), "%s/export/%s.pfx", p, oname);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0644);
len = snprintf(src, sizeof(src), "%s/private/%s.key",
ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
len = snprintf(dst, sizeof(dst), "%s/private/%s.key", p, keyname);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0600);
len = snprintf(dst, sizeof(dst), "%s/private/local.key", p);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0600);
len = snprintf(src, sizeof(src), "%s/%s.crt", ca->sslpath,
keyname);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
len = snprintf(dst, sizeof(dst), "%s/certs/%s.crt", p, keyname);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0644);
len = snprintf(dst, sizeof(dst), "%s/local.pub", p);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
char *cmd[] = { PATH_OPENSSL, "rsa", "-out", dst, "-in", key,
"-pubout", NULL };
ca_execv(cmd);
}
if (stat(PATH_TAR, &st) == 0) {
len = snprintf(src, sizeof(src), "%s.tgz", oname);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
if (keyname == NULL) {
char *cmd[] = { PATH_TAR, "-zcf", src,
"-C", ca->sslpath, ".", NULL };
ca_execv(cmd);
} else {
char *cmd[] = { PATH_TAR, "-zcf", src, "-C", p, ".",
NULL };
ca_execv(cmd);
}
if (realpath(src, dst) != NULL)
printf("exported files in %s\n", dst);
}
if (stat(PATH_ZIP, &st) == 0) {
dexp = opendir(EXPDIR);
if (dexp) {
while ((de = readdir(dexp)) != NULL) {
if (!strcmp(de->d_name, ".") ||
!strcmp(de->d_name, ".."))
continue;
len = snprintf(src, sizeof(src), "%s/%s",
EXPDIR, de->d_name);
if (len < 0 || (size_t)len >= sizeof(src))
err(1, "%s: snprintf", __func__);
len = snprintf(dst, sizeof(dst), "%s/export/%s",
p, de->d_name);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
fcopy(src, dst, 0644);
}
closedir(dexp);
}
len = snprintf(dst, sizeof(dst), "%s/export", p);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
if (getcwd(src, sizeof(src)) == NULL)
err(1, "could not get cwd");
if (chdir(dst) == -1)
err(1, "could not change %s", dst);
len = snprintf(dst, sizeof(dst), "%s/%s.zip", src, oname);
if (len < 0 || (size_t)len >= sizeof(dst))
err(1, "%s: snprintf", __func__);
char *cmd[] = { PATH_ZIP, "-qr", dst, ".", NULL };
ca_execv(cmd);
printf("exported files in %s\n", dst);
if (chdir(src) == -1)
err(1, "could not change %s", dst);
}
rm_dir(p);
return (0);
}
/* create index if it doesn't already exist */
void
ca_create_index(struct ca *ca)
{
struct stat st;
int fd;
int len;
len = snprintf(ca->index, sizeof(ca->index), "%s/index.txt",
ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(ca->index))
err(1, "%s: snprintf", __func__);
if (stat(ca->index, &st) != 0) {
if (errno == ENOENT) {
if ((fd = open(ca->index, O_WRONLY | O_CREAT, 0644))
== -1)
err(1, "could not create file %s", ca->index);
close(fd);
} else
err(1, "could not access %s", ca->index);
}
len = snprintf(ca->serial, sizeof(ca->serial), "%s/serial.txt",
ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(ca->serial))
err(1, "%s: snprintf", __func__);
if (stat(ca->serial, &st) != 0) {
if (errno == ENOENT) {
if ((fd = open(ca->serial, O_WRONLY | O_CREAT, 0644))
== -1)
err(1, "could not create file %s", ca->serial);
/* serial file must be created with a number */
if (write(fd, "01\n", 3) != 3)
err(1, "write %s", ca->serial);
close(fd);
} else
err(1, "could not access %s", ca->serial);
}
}
int
ca_revoke(struct ca *ca, char *keyname)
{
struct stat st;
char path[PATH_MAX];
char cakey[PATH_MAX];
char cacrt[PATH_MAX];
size_t len;
if (keyname) {
len = snprintf(path, sizeof(path), "%s/%s.crt",
ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(path))
err(1, "%s: snprintf", __func__);
if (stat(path, &st) != 0) {
warn("Problem with certificate for '%s'", keyname);
return (1);
}
}
ca_create_index(ca);
ca_setenv("$ENV::CADB", ca->index);
ca_setenv("$ENV::CASERIAL", ca->serial);
if (keyname)
ca_setenv("$ENV::REQ_EXT", "");
ca_setcnf(ca, "ca-revoke");
len = snprintf(cakey, sizeof(cakey), "%s/private/ca.key", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(cakey))
err(1, "%s: snprintf", __func__);
len = snprintf(cacrt, sizeof(cacrt), "%s/ca.crt", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(cacrt))
err(1, "%s: snprintf", __func__);
if (keyname) {
char *cmd[] = { PATH_OPENSSL, "ca", "-config", ca->sslcnf,
"-keyfile", cakey, "-passin", ca->passfile, "-cert", cacrt,
"-revoke", path, ca->batch, NULL };
ca_execv(cmd);
}
len = snprintf(path, sizeof(path), "%s/ca.crl", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(path))
err(1, "%s: snprintf", __func__);
char *cmd[] = { PATH_OPENSSL, "ca", "-config", ca->sslcnf,
"-keyfile", cakey, "-passin", ca->passfile, "-gencrl",
"-cert", cacrt, "-crldays", "365", "-out", path, ca->batch, NULL };
ca_execv(cmd);
return (0);
}
void
ca_clrenv(void)
{
int i;
for (i = 0; ca_env[i][0] != NULL; i++) {
free(ca_env[i][1]);
ca_env[i][1] = NULL;
}
}
void
ca_setenv(const char *key, const char *value)
{
int i;
char *p = NULL;
for (i = 0; ca_env[i][0] != NULL; i++) {
if (strcmp(ca_env[i][0], key) == 0) {
if (ca_env[i][1] != NULL)
errx(1, "env %s already set: %s", key, value);
p = strdup(value);
if (p == NULL)
err(1, NULL);
ca_env[i][1] = p;
return;
}
}
errx(1, "env %s invalid", key);
}
void
ca_setcnf(struct ca *ca, const char *keyname)
{
struct stat st;
const char *extcnf, *sslcnf;
int len;
if (stat(IKECA_CNF, &st) == 0) {
extcnf = IKECA_CNF;
sslcnf = IKECA_CNF;
} else {
extcnf = X509_CNF;
sslcnf = SSL_CNF;
}
len = snprintf(ca->extcnf, sizeof(ca->extcnf), "%s/%s-ext.cnf",
ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(ca->extcnf))
err(1, "%s: snprintf", __func__);
len = snprintf(ca->sslcnf, sizeof(ca->sslcnf), "%s/%s-ssl.cnf",
ca->sslpath, keyname);
if (len < 0 || (size_t)len >= sizeof(ca->sslcnf))
err(1, "%s: snprintf", __func__);
fcopy_env(extcnf, ca->extcnf, 0400);
fcopy_env(sslcnf, ca->sslcnf, 0400);
}
struct ca *
ca_setup(char *caname, int create, int quiet, char *pass)
{
struct stat st;
struct ca *ca;
char path[PATH_MAX];
int len;
if (stat(PATH_OPENSSL, &st) == -1)
err(1, "openssl binary not available");
if ((ca = calloc(1, sizeof(struct ca))) == NULL)
err(1, "calloc");
ca->caname = strdup(caname);
len = snprintf(ca->sslpath, sizeof(ca->sslpath), SSLDIR "/%s", caname);
if (len < 0 || (size_t)len >= sizeof(ca->sslpath))
err(1, "%s: snprintf", __func__);
if (quiet)
ca->batch = "-batch";
if (create == 0 && stat(ca->sslpath, &st) == -1) {
free(ca->caname);
free(ca);
errx(1, "CA '%s' does not exist", caname);
}
strlcpy(path, ca->sslpath, sizeof(path));
if (mkdir(path, 0777) == -1 && errno != EEXIST)
err(1, "failed to create dir %s", path);
strlcat(path, "/private", sizeof(path));
if (mkdir(path, 0700) == -1 && errno != EEXIST)
err(1, "failed to create dir %s", path);
len = snprintf(path, sizeof(path), "%s/ikeca.passwd", ca->sslpath);
if (len < 0 || (size_t)len >= sizeof(path))
err(1, "%s: snprintf", __func__);
if (create && stat(path, &st) == -1 && errno == ENOENT)
ca_newpass(path, pass);
len = snprintf(ca->passfile, sizeof(ca->passfile), "file:%s", path);
if (len < 0 || (size_t)len >= sizeof(ca->passfile))
err(1, "%s: snprintf", __func__);
return (ca);
}
int static
ca_execv(char *const argv[])
{
pid_t pid, cpid;
int status;
switch (cpid = fork()) {
case -1:
return -1;
case 0:
execv(argv[0], argv);
_exit(127);
}
do {
pid = waitpid(cpid, &status, 0);
} while (pid == -1 && errno == EINTR);
return (pid == -1 ? -1 : WEXITSTATUS(status));
}