On Wed, Aug 19, 2015 at 02:04:47PM +1000, Jonathan Gray wrote:
> On Tue, Aug 18, 2015 at 09:22:14PM +0200, Reyk Floeter wrote:
> > On Tue, Aug 18, 2015 at 02:26:29PM +0000, Jona Joachim wrote:
> > > Hi,
> > > I'm currently trying to setup a road warrior IKEv2 IPSEC tunnel between
> > > two OpenBSD boxes running a recent amd64 snapshot. The client is behing
> > > a NAT.
> > > The setup works with a PSK but I cannot make it work with RSA
> > > certificates. No matter what I tried, the client seems to fail
> > > connecting with:
> > > ca_getreq: no valid local certificate found
> > > 
> > > I turn to the mailing list to see if anybody can point me into the right
> > > direction.
> > > 
> > > I loosely followed the following guide:
> > > http://puffysecurity.com/wiki/openikedoffshore.html
> > > I will try to shorten the command output to make it more readable.
> > > 
> > > There is an OpenSSL error during the creation of the CA concerning a
> > > missing element in openssl.cnf. I did not modify openssl.cnf.
> > > 
> > > On the server side I did the following:
> > > 
> > > # ikectl ca ikeca create 
> > > [...]
> > > Signature ok
> > > subject=/C=NL/CN=ikeca/emailAddress=j...@joachim.cc
> > > Getting Private key
> > > Using configuration from /etc/ssl/openssl.cnf
> > > variable lookup failed for ca::default_ca
> > > 7504668282756:error:0E06D06C:configuration file
> > > routines:NCONF_get_string:no
> > > value:/usr/src/lib/libcrypto/crypto/../../libssl/src/crypto/conf/conf_lib.c:323:group=ca
> > > name=default_ca
> > > 
> > 
> > It seems that the changes in LibreSSL (or newer OpenSSL before the
> > fork) broke some things in ikectl.
> > 
> > Specifically, the possibility to overwrite variables like CERTIP or
> > CERTFQDN via $ENV:: options in x509v3.cnf ikeca.cnf* seems to be
> > broken; or not longer supported because of security concerns.
> > 
> > Your log file gives a hint that the default "CERTFQDN = nohost.nodomain"
> > value from /etc/ssl/x509v3.cnf (or /etc/ssl/ikeca.cnf) is used instead
> > of the CERTFQDN overwrite from the environment (as set by ikectl):
> > 
> > > ca_getreq: found CA /C=NL/CN=ikeca/emailAddress=j...@joachim.cc
> > > ca_x509_subjectaltname: FQDN/nohost.nodomain
> > > ca_x509_subjectaltname_cmp: FQDN/nohost.nodomain mismatched
> > > ca_getreq: no valid local certificate found
> > 
> > If libressl no longer supports $ENV in the .cnf files, we have to find
> > another way, eg. by generating and using a .cnf file for each
> > certificate.
> 
> LibreSSL purposefully removed support for environment variables in
> http://marc.info/?l=openbsd-cvs&m=142876823016723&w=2
> http://marc.info/?l=openbsd-cvs&m=142876823016723&w=2
> 
> So another way is indeed needed.

In this case, "LibreSSL" was Theo who unintentionally broke ikectl.

I attached a diff that generates new .cnf files by expanding the
variables in the source .cnf files and generating target .cnf files.
It works with both, ikeca.cnf and x508v3.cnf (ignore the warnings),
but you/we should install ikeca.cnf to /etc/ssl/ by default.

There are more pending changes for ikectl (eg. from semarie@), but I'd
like to fix this first.

OK?

Reyk

Index: Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/ikectl/Makefile,v
retrieving revision 1.3
diff -u -p -u -p -r1.3 Makefile
--- Makefile    18 Jan 2014 05:54:51 -0000      1.3
+++ Makefile    19 Aug 2015 08:12:39 -0000
@@ -3,7 +3,7 @@
 .PATH:         ${.CURDIR}/../../sbin/iked
 
 PROG=          ikectl
-SRCS=          log.c ikeca.c ikectl.c parser.c
+SRCS=          log.c ikeca.c ikectl.c parser.c util.c
 
 MAN=           ikectl.8
 
Index: ikeca.c
===================================================================
RCS file: /cvs/src/usr.sbin/ikectl/ikeca.c,v
retrieving revision 1.32
diff -u -p -u -p -r1.32 ikeca.c
--- ikeca.c     15 Aug 2015 04:47:28 -0000      1.32
+++ ikeca.c     19 Aug 2015 08:12:39 -0000
@@ -82,13 +82,39 @@ struct {
        { "/private",   0700 }
 };
 
-int             ca_sign(struct ca *, char *, int, char *);
+/* explicitly list allowed variables */
+const char *ca_env[][2] = {
+       { "$ENV::CADB", 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 },
+       { NULL }
+};
+
+int             ca_sign(struct ca *, char *, int);
 int             ca_request(struct ca *, char *);
 int             ca_newpass(char *, char *);
 char *          ca_readpass(char *, size_t *);
 int             fcopy(char *, char *, mode_t);
+int             fcopy_env(const char *, const char *, mode_t);
 int             rm_dir(char *);
 int             ca_hier(char *);
+void            ca_setenv(const char *, const char *);
+void            ca_clrenv(void);
+void            ca_setcnf(struct ca *, const char *);
+
+/* util.c */
+int             expand_string(char *, size_t, const char *, const char *);
 
 int
 ca_delete(struct ca *ca)
@@ -173,10 +199,13 @@ ca_request(struct ca *ca, char *keyname)
        char            cmd[PATH_MAX * 2];
        char            path[PATH_MAX];
 
+       ca_setenv("$ENV::CERT_CN", keyname);
+       ca_setcnf(ca, keyname);
+
        snprintf(path, sizeof(path), "%s/private/%s.csr", ca->sslpath, keyname);
-       snprintf(cmd, sizeof(cmd), "env CERT_CN=%s %s req %s-new"
+       snprintf(cmd, sizeof(cmd), "%s req %s-new"
            " -key %s/private/%s.key -out %s -config %s",
-           keyname, PATH_OPENSSL, ca->batch, ca->sslpath, keyname,
+           PATH_OPENSSL, ca->batch, ca->sslpath, keyname,
            path, ca->sslcnf);
 
        system(cmd);
@@ -186,40 +215,40 @@ ca_request(struct ca *ca, char *keyname)
 }
 
 int
-ca_sign(struct ca *ca, char *keyname, int type, char *envargs)
+ca_sign(struct ca *ca, char *keyname, int type)
 {
        char            cmd[PATH_MAX * 2];
        char            hostname[HOST_NAME_MAX+1];
        char            name[128];
+       const char      *extensions = NULL;
 
        strlcpy(name, keyname, sizeof(name));
 
-       if (envargs == NULL)
-               envargs = "";
-
        if (type == HOST_IPADDR) {
-               snprintf(cmd, sizeof(cmd), "env CERTIP=%s%s %s x509 -req"
-                   " -days 365 -in %s/private/%s.csr"
-                   " -CA %s/ca.crt -CAkey %s/private/ca.key -CAcreateserial"
-                   " -extfile %s -extensions x509v3_IPAddr -out %s/%s.crt"
-                   " -passin file:%s", name, envargs, PATH_OPENSSL,
-                   ca->sslpath, keyname, ca->sslpath, ca->sslpath,
-                   ca->extcnf, ca->sslpath, keyname, ca->passfile);
+               ca_setenv("$ENV::CERTIP", name);
+               extensions = "x509v3_IPAddr";
        } else if (type == HOST_FQDN) {
                if (!strcmp(keyname, "local")) {
                        if (gethostname(hostname, sizeof(hostname)))
                                err(1, "gethostname");
                        strlcpy(name, hostname, sizeof(name));
                }
-               snprintf(cmd, sizeof(cmd), "env CERTFQDN=%s%s %s x509 -req"
-                   " -days 365 -in %s/private/%s.csr"
-                   " -CA %s/ca.crt -CAkey %s/private/ca.key -CAcreateserial"
-                   " -extfile %s -extensions x509v3_FQDN -out %s/%s.crt"
-                   " -passin file:%s", name, envargs, PATH_OPENSSL,
-                   ca->sslpath, keyname, ca->sslpath, ca->sslpath,
-                   ca->extcnf, ca->sslpath, keyname, ca->passfile);
-       } else
+               ca_setenv("$ENV::CERTFQDN", name);
+               extensions = "x509v3_FQDN";
+       } else {
                errx(1, "unknown host type %d", type);
+       }
+
+       ca_setcnf(ca, keyname);
+
+       snprintf(cmd, sizeof(cmd), "%s x509 -req"
+           " -days 365 -in %s/private/%s.csr"
+           " -CA %s/ca.crt -CAkey %s/private/ca.key -CAcreateserial"
+           " -extfile %s -extensions %s -out %s/%s.crt"
+           " -passin file:%s",
+           PATH_OPENSSL,
+           ca->sslpath, keyname, ca->sslpath, ca->sslpath,
+           ca->extcnf, extensions, ca->sslpath, keyname, ca->passfile);
 
        system(cmd);
 
@@ -229,16 +258,20 @@ ca_sign(struct ca *ca, char *keyname, in
 int
 ca_certificate(struct ca *ca, char *keyname, int type, int action)
 {
-       char    *envargs = "";
+       ca_clrenv();
 
        switch (action) {
        case CA_SERVER:
-               envargs = " EXTCERTUSAGE=serverAuth NSCERTTYPE=server"
-                   " CERTUSAGE=digitalSignature,keyEncipherment";
+               ca_setenv("$ENV::EXTCERTUSAGE", "serverAuth");
+               ca_setenv("$ENV::NSCERTTYPE", "server");
+               ca_setenv("$ENV::CERTUSAGE",
+                   "digitalSignature,keyEncipherment");
                break;
        case CA_CLIENT:
-               envargs = " EXTCERTUSAGE=clientAuth NSCERTTYPE=client"
-                   " CERTUSAGE=digitalSignature,keyAgreement";
+               ca_setenv("$ENV::EXTCERTUSAGE", "clientAuth");
+               ca_setenv("$ENV::NSCERTTYPE", "client");
+               ca_setenv("$ENV::CERTUSAGE",
+                   "digitalSignature,keyAgreement");
                break;
        default:
                break;
@@ -246,7 +279,7 @@ ca_certificate(struct ca *ca, char *keyn
 
        ca_key_create(ca, keyname);
        ca_request(ca, keyname);
-       ca_sign(ca, keyname, type, envargs);
+       ca_sign(ca, keyname, type);
 
        return (0);
 }
@@ -352,6 +385,8 @@ ca_create(struct ca *ca)
        char                     cmd[PATH_MAX * 2];
        char                     path[PATH_MAX];
 
+       ca_clrenv();
+
        snprintf(path, sizeof(path), "%s/private/ca.key", ca->sslpath);
        snprintf(cmd, sizeof(cmd), "%s genrsa -aes256 -out"
            " %s -passout file:%s 2048", PATH_OPENSSL,
@@ -359,8 +394,11 @@ ca_create(struct ca *ca)
        system(cmd);
        chmod(path, 0600);
 
+       ca_setenv("$ENV::CERT_CN", "VPN CA");
+       ca_setcnf(ca, "ca");
+
        snprintf(path, sizeof(path), "%s/private/ca.csr", ca->sslpath);
-       snprintf(cmd, sizeof(cmd), "env CERT_CN='VPN CA' %s req %s-new"
+       snprintf(cmd, sizeof(cmd), "%s req %s-new"
            " -key %s/private/ca.key"
            " -config %s -out %s -passin file:%s", PATH_OPENSSL,
            ca->batch, ca->sslpath, ca->sslcnf, path, ca->passfile);
@@ -489,6 +527,46 @@ fcopy(char *src, char *dst, mode_t mode)
 }
 
 int
+fcopy_env(const char *src, const char *dst, mode_t mode)
+{
+       int              ofd = -1, i;
+       u_int8_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) != 0) {
+               for (i = 0; ca_env[i][0] != NULL; i++) {
+                       if (ca_env[i][1] == NULL)
+                               continue;
+                       expand_string(buf, sizeof(buf),
+                           ca_env[i][0], ca_env[i][1]);
+               }
+               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);
+
+       return (0);
+}
+
+int
 rm_dir(char *path)
 {
        FTS             *fts;
@@ -742,6 +820,7 @@ ca_revoke(struct ca *ca, char *keyname)
        struct stat      st;
        char             cmd[PATH_MAX * 2];
        char             path[PATH_MAX];
+       char             cadb[PATH_MAX];
        int              fd;
        char            *pass;
        size_t           len;
@@ -771,27 +850,31 @@ ca_revoke(struct ca *ca, char *keyname)
                        err(1, "could not access %s", path);
        }
 
+       snprintf(cadb, sizeof(cadb), "%s/index.txt", ca->sslpath);
+       ca_setenv("$ENV::CADB", cadb);
+       ca_setcnf(ca, "ca-revoke");
+
        if (keyname) {
-               snprintf(cmd, sizeof(cmd), "env CADB='%s/index.txt' "
-                   " %s ca %s-config %s -keyfile %s/private/ca.key"
+               snprintf(cmd, sizeof(cmd),
+                   "%s ca %s-config %s -keyfile %s/private/ca.key"
                    " -key %s"
                    " -cert %s/ca.crt"
                    " -md sha1"
                    " -revoke %s/%s.crt",
-                   ca->sslpath, PATH_OPENSSL, ca->batch, ca->sslcnf,
+                   PATH_OPENSSL, ca->batch, ca->sslcnf,
                    ca->sslpath, pass, ca->sslpath, ca->sslpath, keyname);
                system(cmd);
        }
 
-       snprintf(cmd, sizeof(cmd), "env CADB='%s/index.txt' "
-           " %s ca %s-config %s -keyfile %s/private/ca.key"
+       snprintf(cmd, sizeof(cmd),
+           "%s ca %s-config %s -keyfile %s/private/ca.key"
            " -key %s"
            " -gencrl"
            " -cert %s/ca.crt"
            " -md sha1"
            " -crldays 365"
            " -out %s/ca.crl",
-           ca->sslpath, PATH_OPENSSL, ca->batch, ca->sslcnf, ca->sslpath,
+           PATH_OPENSSL, ca->batch, ca->sslcnf, ca->sslpath,
            pass, ca->sslpath, ca->sslpath);
        system(cmd);
 
@@ -801,6 +884,53 @@ ca_revoke(struct ca *ca, char *keyname)
        return (0);
 }
 
+void
+ca_clrenv(void)
+{
+       int      i;
+       for (i = 0; ca_env[i][0] != NULL; i++)
+               ca_env[i][1] = NULL;
+}
+
+void
+ca_setenv(const char *key, const char *value)
+{
+       int      i;
+
+       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);
+                       ca_env[i][1] = value;
+                       return;
+               }
+       }
+       errx(1, "env %s invalid", key);
+}
+
+void
+ca_setcnf(struct ca *ca, const char *keyname)
+{
+       struct stat      st;
+       const char      *extcnf, *sslcnf;
+
+       if (stat(IKECA_CNF, &st) == 0) {
+               extcnf = IKECA_CNF;
+               sslcnf = IKECA_CNF;
+       } else {
+               extcnf = X509_CNF;
+               sslcnf = SSL_CNF;
+       }
+
+       snprintf(ca->extcnf, sizeof(ca->extcnf), "%s/%s-ext.cnf",
+           ca->sslpath, keyname);
+       snprintf(ca->sslcnf, sizeof(ca->sslcnf), "%s/%s-ssl.cnf",
+           ca->sslpath, keyname);
+
+       fcopy_env(extcnf, ca->extcnf, 0644);
+       fcopy_env(sslcnf, ca->sslcnf, 0644);
+}
+
 struct ca *
 ca_setup(char *caname, int create, int quiet, char *pass)
 {
@@ -821,14 +951,6 @@ ca_setup(char *caname, int create, int q
 
        if (quiet)
                strlcpy(ca->batch, "-batch ", sizeof(ca->batch));
-
-       if (stat(IKECA_CNF, &st) == 0) {
-               strlcpy(ca->extcnf, IKECA_CNF, sizeof(ca->extcnf));
-               strlcpy(ca->sslcnf, IKECA_CNF, sizeof(ca->sslcnf));
-       } else {
-               strlcpy(ca->extcnf, X509_CNF, sizeof(ca->extcnf));
-               strlcpy(ca->sslcnf, SSL_CNF, sizeof(ca->sslcnf));
-       }
 
        if (create == 0 && stat(ca->sslpath, &st) == -1) {
                free(ca->caname);

Reply via email to