Hi,

Two diffs below. The first moves ecdsa_method declaration from
ecs_locl.h to ecdsa.h, as ecs_locl.h is not installed in
/usr/include/openssl/.

The second one adds DSA and ECDSA capabilities to relayd ca engine, and
also checks that when using a DSA certificate, we have enabled EDH in
the relevant proto section. Requirements have been documented in
relayd.conf(5).

It works, but it surely needs some refactoring and style tweaks.

Comments ?

[Diff #1]

Index: ecdsa.h
===================================================================
RCS file: /cvs/src/lib/libssl/src/crypto/ecdsa/ecdsa.h,v
retrieving revision 1.2
diff -u -p -u -r1.2 ecdsa.h
--- ecdsa.h     12 Jun 2014 15:49:29 -0000      1.2
+++ ecdsa.h     8 Nov 2014 18:30:58 -0000
@@ -75,11 +75,36 @@
 extern "C" {
 #endif
 
-typedef struct ECDSA_SIG_st
-       {
+typedef struct ECDSA_SIG_st ECDSA_SIG;
+
+struct ecdsa_method {
+       const char *name;
+       ECDSA_SIG *(*ecdsa_do_sign)(const unsigned char *dgst, int dgst_len, 
+           const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey);
+       int (*ecdsa_sign_setup)(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, 
+           BIGNUM **r);
+       int (*ecdsa_do_verify)(const unsigned char *dgst, int dgst_len, 
+           const ECDSA_SIG *sig, EC_KEY *eckey);
+#if 0
+       int (*init)(EC_KEY *eckey);
+       int (*finish)(EC_KEY *eckey);
+#endif
+       int flags;
+       char *app_data;
+};
+
+/* If this flag is set the ECDSA method is FIPS compliant and can be used
+ * in FIPS mode. This is set in the validated module method. If an
+ * application sets this flag in its own methods it is its responsibility
+ * to ensure the result is compliant.
+ */
+
+#define ECDSA_FLAG_FIPS_METHOD  0x1
+
+struct ECDSA_SIG_st {
        BIGNUM *r;
        BIGNUM *s;
-       } ECDSA_SIG;
+};
 
 /** Allocates and initialize a ECDSA_SIG structure
  *  \return pointer to a ECDSA_SIG structure or NULL if an error occurred
Index: ecs_locl.h
===================================================================
RCS file: /cvs/src/lib/libssl/src/crypto/ecdsa/ecs_locl.h,v
retrieving revision 1.2
diff -u -p -u -r1.2 ecs_locl.h
--- ecs_locl.h  12 Jun 2014 15:49:29 -0000      1.2
+++ ecs_locl.h  8 Nov 2014 18:30:58 -0000
@@ -65,31 +65,6 @@
 extern "C" {
 #endif
 
-struct ecdsa_method 
-       {
-       const char *name;
-       ECDSA_SIG *(*ecdsa_do_sign)(const unsigned char *dgst, int dgst_len, 
-                       const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey);
-       int (*ecdsa_sign_setup)(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, 
-                       BIGNUM **r);
-       int (*ecdsa_do_verify)(const unsigned char *dgst, int dgst_len, 
-                       const ECDSA_SIG *sig, EC_KEY *eckey);
-#if 0
-       int (*init)(EC_KEY *eckey);
-       int (*finish)(EC_KEY *eckey);
-#endif
-       int flags;
-       char *app_data;
-       };
-
-/* If this flag is set the ECDSA method is FIPS compliant and can be used
- * in FIPS mode. This is set in the validated module method. If an
- * application sets this flag in its own methods it is its responsibility
- * to ensure the result is compliant.
- */
-
-#define ECDSA_FLAG_FIPS_METHOD 0x1
-
 typedef struct ecdsa_data_st {
        /* EC_KEY_METH_DATA part */
        int (*init)(EC_KEY *);


[Diff #2]

Index: ca.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/ca.c,v
retrieving revision 1.9
diff -u -p -u -r1.9 ca.c
--- ca.c        2 Oct 2014 19:16:31 -0000       1.9
+++ ca.c        8 Nov 2014 18:26:15 -0000
@@ -32,9 +32,13 @@
 #include <stdlib.h>
 #include <errno.h>
 
+#include <openssl/ossl_typ.h>
+#include <openssl/bn.h>
 #include <openssl/pem.h>
 #include <openssl/evp.h>
 #include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/ecdsa.h>
 #include <openssl/engine.h>
 
 #include "relayd.h"
@@ -60,6 +64,30 @@ int   rsae_verify(int dtype, const u_char
            u_int, const RSA *);
 int     rsae_keygen(RSA *, int, BIGNUM *, BN_GENCB *);
 
+
+DSA_SIG                *dsae_do_sign(const unsigned char *, int, DSA *);
+int             dsae_sign_setup(DSA *, BN_CTX *, BIGNUM **, BIGNUM **);
+int             dsae_do_verify(const unsigned char *, int, DSA_SIG *,
+                   DSA *);
+int             dsae_mod_exp(DSA *, BIGNUM *, BIGNUM *, BIGNUM *,
+                   BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *,
+                   BN_MONT_CTX *);
+int             dsae_bn_mod_exp(DSA *, BIGNUM *, BIGNUM *,
+                   const BIGNUM *, const BIGNUM *, BN_CTX *,
+                   BN_MONT_CTX *);
+int             dsae_init(DSA *);
+int             dsae_finish(DSA *);
+int             dsae_paramgen(DSA *, int, const unsigned char *, int,
+                   int *, unsigned long *, BN_GENCB *);
+int             dsae_keygen(DSA *);
+
+ECDSA_SIG      *ecdsae_do_sign(const unsigned char *, int ,
+                   const BIGNUM *, const BIGNUM *, EC_KEY *);
+int             ecdsae_sign_setup(EC_KEY *, BN_CTX *, BIGNUM **,
+                   BIGNUM **);
+int             ecdsae_do_verify(const unsigned char *, int,
+                   const ECDSA_SIG *, EC_KEY *);
+
 static struct relayd *env = NULL;
 extern int              proc_id;
 
@@ -174,14 +202,22 @@ ca_dispatch_relay(int fd, struct privsep
 {
        struct ctl_keyop         cko;
        EVP_PKEY                *pkey;
-       RSA                     *rsa;
-       u_char                  *from = NULL, *to = NULL;
+       RSA                     *rsa = NULL;
+       DSA                     *dsa = NULL;
+       EC_KEY                  *ecdsa = NULL;
+       DSA_SIG                 *dsa_sig = NULL;
+       ECDSA_SIG               *ecdsa_sig = NULL;
+       u_char                  *from = NULL, *to = NULL, *toptr = NULL;
+       u_char                  *k_der = NULL, *r_der = NULL;
+       BIGNUM                  *k = NULL, *r = NULL, *k_orig, *r_orig;
        struct iovec             iov[2];
        int                      c = 0;
 
        switch (imsg->hdr.type) {
        case IMSG_CA_PRIVENC:
        case IMSG_CA_PRIVDEC:
+       case IMSG_CA_DSA_SIGN:
+       case IMSG_CA_ECDSA_SIGN:
                IMSG_SIZE_CHECK(imsg, (&cko));
                bcopy(imsg->data, &cko, sizeof(cko));
                if (cko.cko_proc > env->sc_prefork_relay)
@@ -190,25 +226,89 @@ ca_dispatch_relay(int fd, struct privsep
                if (IMSG_DATA_SIZE(imsg) != (sizeof(cko) + cko.cko_flen))
                        fatalx("ca_dispatch_relay: "
                            "invalid key operation");
-               if ((pkey = pkey_find(env, cko.cko_id)) == NULL ||
-                   (rsa = EVP_PKEY_get1_RSA(pkey)) == NULL)
+
+               if ((pkey = pkey_find(env, cko.cko_id)) == NULL)
                        fatalx("ca_dispatch_relay: "
-                           "invalid relay key or id");
+                           "invalid relay key id");
 
-               DPRINTF("%s:%d: key id %d", __func__, __LINE__, cko.cko_id);
+               DPRINTF("%s:%d: key id %d, type %d", __func__, __LINE__, 
cko.cko_id, pkey->save_type);
+
+               switch (imsg->hdr.type) {
+               case IMSG_CA_PRIVENC:
+               case IMSG_CA_PRIVDEC:
+                       if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL)
+                               fatalx("ca_dispatch_relay: "
+                                   "invalid relay key type");
+                       if ((cko.cko_klen != 0) ||
+                           (cko.cko_rlen != 0))
+                               fatalx("ca_dispatch_relay: "
+                                   "invalid parameters, should be zeroed");
+                       break;
+               case IMSG_CA_DSA_SIGN:
+                       if ((dsa = EVP_PKEY_get1_DSA(pkey)) == NULL)
+                               fatalx("ca_dispatch_relay: "
+                                   "invalid relay key type");
+                       break;
+               case IMSG_CA_ECDSA_SIGN:
+                       if ((ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL)
+                               fatalx("ca_dispatch_relay: "
+                                   "invalid relay key type");
+                       break;
+               }
 
                from = (u_char *)imsg->data + sizeof(cko);
                if ((to = calloc(1, cko.cko_tlen)) == NULL)
                        fatalx("ca_dispatch_relay: calloc");
+               toptr = to;
+
+               k_der = from + cko.cko_flen;
+               if ((cko.cko_klen > 0) &&
+                   (k = BN_bin2bn(k_der, cko.cko_klen, NULL)) == NULL)
+                       fatalx("ca_dispatch_relay: invalid k");
+
+               r_der = k_der + cko.cko_klen;
+               if ((cko.cko_rlen > 0) &&
+                   (r = BN_bin2bn(r_der, cko.cko_rlen, NULL)) == NULL)
+                       fatalx("ca_dispatch_relay: invalid r");
 
                switch (imsg->hdr.type) {
                case IMSG_CA_PRIVENC:
                        cko.cko_tlen = RSA_private_encrypt(cko.cko_flen,
                            from, to, rsa, cko.cko_padding);
+                       RSA_free(rsa);
                        break;
                case IMSG_CA_PRIVDEC:
                        cko.cko_tlen = RSA_private_decrypt(cko.cko_flen,
                            from, to, rsa, cko.cko_padding);
+                       RSA_free(rsa);
+                       break;
+               case IMSG_CA_DSA_SIGN:
+                       k_orig = dsa->kinv;
+                       dsa->kinv = k;
+                       r_orig = dsa->r;
+                       dsa->r = r;
+                       dsa_sig = DSA_do_sign(from, cko.cko_flen, dsa);
+                       if (dsa_sig == NULL)
+                               fatalx("ca_dispatch_relay: DSA_do_sign");
+                       cko.cko_tlen = i2d_DSA_SIG(dsa_sig, &toptr);
+                       dsa->kinv = k_orig;
+                       dsa->r = r_orig;
+                       DSA_SIG_free(dsa_sig);
+                       DSA_free(dsa);
+                       BN_free(k);
+                       BN_free(r);
+                       break;
+               case IMSG_CA_ECDSA_SIGN:
+                       ecdsa_sig = ECDSA_do_sign_ex(from, cko.cko_flen, k,
+                           r, ecdsa);
+                       if (ecdsa_sig == NULL)
+                               fatalx("ca_dispatch_relay: "
+                                   "ECDSA_do_sign_ex");
+                       cko.cko_tlen = i2d_ECDSA_SIG(ecdsa_sig, &toptr);
+                       ECDSA_SIG_free(ecdsa_sig);
+                       EC_KEY_free(ecdsa);
+                       BN_free(k);
+                       BN_free(r);
                        break;
                }
 
@@ -223,7 +323,6 @@ ca_dispatch_relay(int fd, struct privsep
                    imsg->hdr.type, -1, iov, c);
 
                free(to);
-               RSA_free(rsa);
                break;
        default:
                return (-1);
@@ -284,6 +383,8 @@ rsae_send_imsg(int flen, const u_char *f
        cko.cko_flen = flen;
        cko.cko_tlen = RSA_size(rsa);
        cko.cko_padding = padding;
+       cko.cko_klen = 0;
+       cko.cko_rlen = 0;
 
        iov[cnt].iov_base = &cko;
        iov[cnt++].iov_len = sizeof(cko);
@@ -420,6 +521,335 @@ rsae_keygen(RSA *rsa, int bits, BIGNUM *
        return (rsa_default->rsa_keygen(rsa, bits, e, cb));
 }
 
+/*
+ * DSA privsep engine (called from unprivileged processes)
+ */
+
+const DSA_METHOD *dsa_default = NULL;
+
+static DSA_METHOD dsae_method = {
+       "DSA privsep method",
+       dsae_do_sign,
+       dsae_sign_setup,
+       dsae_do_verify,
+       dsae_mod_exp,
+       dsae_bn_mod_exp,
+       dsae_init,
+       dsae_finish,
+       0,
+       NULL,
+       dsae_paramgen,
+       dsae_keygen
+};
+
+DSA_SIG *
+dsae_do_sign(const unsigned char *dgst, int dlen, DSA *dsa)
+{
+       /* IMSG call */
+       /* non need to send BN_MONT as it is only used during verify
+        * and we are only going to assume a server role */
+
+       struct ctl_keyop         cko;
+       int                      retlen, klen = 0, rlen = 0;
+       objid_t                 *id;
+       struct iovec             iov[4];
+       struct imsgbuf          *ibuf;
+       struct imsgev           *iev;
+       struct imsg              imsg;
+       int                      n, done = 0, cnt = 0;
+       const unsigned char     *toptr;
+       DSA_SIG                 *retsig = NULL;
+
+       if ((id = DSA_get_ex_data(dsa, 0)) == NULL)
+               return NULL;
+
+       DPRINTF("%s: requesting signing for key %d", __func__, *id);
+
+       iev = proc_iev(env->sc_ps, PROC_CA, proc_id);
+       ibuf = &iev->ibuf;
+
+       /*
+        * XXX this could be nicer...
+        */
+
+       if (dsa->kinv != NULL)
+               klen = BN_num_bytes(dsa->kinv);
+       if (dsa->r != NULL)
+               rlen = BN_num_bytes(dsa->r);
+
+       cko.cko_id = *id;
+       cko.cko_proc = proc_id;
+       cko.cko_flen = dlen;
+       cko.cko_tlen = DSA_size(dsa);
+       cko.cko_padding = 0;
+       cko.cko_klen = klen;
+       cko.cko_rlen = rlen;
+
+       iov[cnt].iov_base = &cko;
+       iov[cnt++].iov_len = sizeof(cko);
+       iov[cnt].iov_base = (void *)dgst;
+       iov[cnt++].iov_len = dlen;
+       if (klen > 0) {
+               iov[cnt].iov_base = calloc(1, klen);
+               if (iov[cnt].iov_base == NULL)
+                       fatalx("dsae_do_sign: calloc");
+               BN_bn2bin(dsa->kinv, iov[cnt].iov_base);
+               iov[cnt++].iov_len = klen;
+       }
+       if (rlen > 0) {
+               iov[cnt].iov_base = calloc(1, rlen);
+               if (iov[cnt].iov_base == NULL)
+                       fatalx("dsae_do_sign: calloc");
+               BN_bn2bin(dsa->r, iov[cnt].iov_base);
+               iov[cnt++].iov_len = rlen;
+       }
+
+       /*
+        * Send a synchronous imsg because we cannot defer the DSA
+        * operation in OpenSSL's engine layer.
+        */
+       imsg_composev(ibuf, IMSG_CA_DSA_SIGN, 0, 0, -1, iov, cnt);
+       imsg_flush(ibuf);
+       if (cnt > 2)
+               free(iov[2].iov_base);
+       if (cnt > 3)
+               free(iov[3].iov_base);
+
+       while (!done) {
+               if ((n = imsg_read(ibuf)) == -1)
+                       fatalx("imsg_read");
+               if (n == 0)
+                       fatalx("pipe closed");
+
+               while (!done) {
+                       if ((n = imsg_get(ibuf, &imsg)) == -1)
+                               fatalx("imsg_get error");
+                       if (n == 0)
+                               break;
+                       if (imsg.hdr.type != IMSG_CA_DSA_SIGN)
+                               fatalx("invalid response");
+
+                       IMSG_SIZE_CHECK(&imsg, (&cko));
+                       memcpy(&cko, imsg.data, sizeof(cko));
+                       if (IMSG_DATA_SIZE(&imsg) !=
+                           (sizeof(cko) + cko.cko_tlen))
+                               fatalx("data size");
+
+                       retlen = cko.cko_tlen;
+                       if (retlen) {
+                               toptr = (u_char *)imsg.data + sizeof(cko);
+                               retsig = d2i_DSA_SIG(NULL,
+                                               &toptr, retlen);
+                       }
+                       done = 1;
+
+                       imsg_free(&imsg);
+               }
+       }
+       imsg_event_add(iev);
+
+       return (retsig);
+}
+
+int
+dsae_sign_setup(DSA *dsa, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (dsa_default->dsa_sign_setup(dsa, ctx_in, kinvp, rp));
+}
+
+int
+dsae_do_verify(const unsigned char *dgst, int dgst_len, DSA_SIG *sig,
+    DSA *dsa)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (dsa_default->dsa_do_verify(dgst, dgst_len, sig, dsa));
+}
+
+int
+dsae_mod_exp(DSA *dsa, BIGNUM *rr, BIGNUM *a1, BIGNUM *p1, BIGNUM *a2,
+    BIGNUM *p2, BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (dsa_default->dsa_mod_exp(dsa, rr, a1, p1, a2, p2, m,
+          ctx, in_mont));
+}
+
+int
+dsae_bn_mod_exp(DSA *dsa, BIGNUM *r, BIGNUM *a, const BIGNUM *p,
+       const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (dsa_default->bn_mod_exp(dsa, r, a, p, m, ctx,
+           m_ctx));
+}
+
+int
+dsae_init(DSA *dsa)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (dsa_default->init(dsa));
+}
+
+int
+dsae_finish(DSA *dsa)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (dsa_default->finish(dsa));
+}
+
+int
+dsae_paramgen(DSA *dsa, int bits, const unsigned char *seed, int seed_len,
+    int *counter_ret, unsigned long *h_ret, BN_GENCB *cb)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (dsa_default->dsa_paramgen(dsa, bits, seed, seed_len,
+           counter_ret, h_ret, cb));
+}
+
+int
+dsae_keygen(DSA *dsa)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (dsa_default->dsa_keygen(dsa));
+}
+
+/*
+ * ECDSA privsep engine (called from unprivileged processes)
+ */
+
+const ECDSA_METHOD *ecdsa_default = NULL;
+
+static ECDSA_METHOD ecdsae_method = {
+       "ECDSA privsep engine",
+       ecdsae_do_sign,
+       ecdsae_sign_setup,
+       ecdsae_do_verify,
+       0,
+       NULL
+};
+
+ECDSA_SIG *
+ecdsae_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
+    const BIGNUM *rp, EC_KEY *eckey)
+{
+       struct ctl_keyop         cko;
+       int                      retlen, klen = 0, rlen = 0;
+       objid_t                 *id;
+       struct iovec             iov[4];
+       struct imsgbuf          *ibuf;
+       struct imsgev           *iev;
+       struct imsg              imsg;
+       int                      n, done = 0, cnt = 0;
+       const unsigned char     *toptr;
+       ECDSA_SIG               *retsig = NULL;
+
+       if ((id = ECDSA_get_ex_data(eckey, 0)) == NULL)
+               return NULL;
+
+       DPRINTF("%s: requesting signing for key %d", __func__, *id);
+
+       iev = proc_iev(env->sc_ps, PROC_CA, proc_id);
+       ibuf = &iev->ibuf;
+
+       /*
+        * XXX this could be nicer...
+        */
+
+       if (inv != NULL)
+               klen = BN_num_bytes(inv);
+       if (rp != NULL)
+               rlen = BN_num_bytes(rp);
+
+       cko.cko_id = *id;
+       cko.cko_proc = proc_id;
+       cko.cko_flen = dgst_len;
+       cko.cko_tlen = ECDSA_size(eckey);
+       cko.cko_padding = 0;
+       cko.cko_klen = klen;
+       cko.cko_rlen = rlen;
+
+       iov[cnt].iov_base = &cko;
+       iov[cnt++].iov_len = sizeof(cko);
+       iov[cnt].iov_base = (void *)dgst;
+       iov[cnt++].iov_len = dgst_len;
+       if (klen > 0) {
+               iov[cnt].iov_base = malloc(klen);
+               if (iov[cnt].iov_base == NULL)
+                       fatalx("ecdsae_do_sign: calloc");
+               BN_bn2bin(inv, iov[cnt].iov_base);
+               iov[cnt++].iov_len = klen;
+       }
+       if (rlen > 0) {
+               iov[cnt].iov_base = malloc(rlen);
+               if (iov[cnt].iov_base == NULL)
+                       fatalx("ecdsae_do_sign: calloc");
+               BN_bn2bin(rp, iov[cnt].iov_base);
+               iov[cnt++].iov_len = rlen;
+       }
+
+       /*
+        * Send a synchronous imsg because we cannot defer the ECDSA
+        * operation in OpenSSL's engine layer.
+        */
+       imsg_composev(ibuf, IMSG_CA_ECDSA_SIGN, 0, 0, -1, iov, cnt);
+       imsg_flush(ibuf);
+       if (cnt > 2)
+               free(iov[2].iov_base);
+       if (cnt > 3)
+               free(iov[3].iov_base);
+
+       while (!done) {
+               if ((n = imsg_read(ibuf)) == -1)
+                       fatalx("imsg_read");
+               if (n == 0)
+                       fatalx("pipe closed");
+
+               while (!done) {
+                       if ((n = imsg_get(ibuf, &imsg)) == -1)
+                               fatalx("imsg_get error");
+                       if (n == 0)
+                               break;
+                       if (imsg.hdr.type != IMSG_CA_ECDSA_SIGN)
+                               fatalx("invalid response");
+
+                       IMSG_SIZE_CHECK(&imsg, (&cko));
+                       memcpy(&cko, imsg.data, sizeof(cko));
+                       if (IMSG_DATA_SIZE(&imsg) !=
+                           (sizeof(cko) + cko.cko_tlen))
+                               fatalx("data size");
+
+                       retlen = cko.cko_tlen;
+                       if (retlen) {
+                               toptr = (u_char *)imsg.data + sizeof(cko);
+                               retsig = d2i_ECDSA_SIG(NULL,
+                                               &toptr, retlen);
+                       }
+                       done = 1;
+
+                       imsg_free(&imsg);
+               }
+       }
+       imsg_event_add(iev);
+
+       return (retsig);
+}
+
+int
+ecdsae_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, BIGNUM **r)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (ecdsa_default->ecdsa_sign_setup(eckey, ctx, kinv, r));
+}
+
+int
+ecdsae_do_verify(const unsigned char *dgst, int dgst_len, const ECDSA_SIG *sig,
+    EC_KEY *eckey)
+{
+       DPRINTF("%s:%d", __func__, __LINE__);
+       return (ecdsa_default->ecdsa_do_verify(dgst, dgst_len, sig, eckey));
+}
+
 void
 ca_engine_init(struct relayd *x_env)
 {
@@ -471,6 +901,88 @@ ca_engine_init(struct relayd *x_env)
        }
        if (!ENGINE_set_default_RSA(e)) {
                errstr = "ENGINE_set_default_RSA";
+               goto fail;
+       }
+
+       /* XXX copy-paste from above, must be a better way */
+
+       if ((e = ENGINE_get_default_DSA()) == NULL) {
+               if ((e = ENGINE_new()) == NULL) {
+                       errstr = "ENGINE_new";
+                       goto fail;
+               }
+               if (!ENGINE_set_name(e, dsae_method.name)) {
+                       errstr = "ENGINE_set_name";
+                       goto fail;
+               }
+               if ((dsa_default = DSA_get_default_method()) == NULL) {
+                       errstr = "DSA_get_default_method";
+                       goto fail;
+               }
+       } else if ((dsa_default = ENGINE_get_DSA(e)) == NULL) {
+               errstr = "ENGINE_get_DSA";
+               goto fail;
+       }
+
+       if ((name = ENGINE_get_name(e)) == NULL)
+               name = "unknown DSA engine";
+
+       log_debug("%s: using %s", __func__, name);
+
+       if (dsa_default->dsa_mod_exp == NULL)
+               dsae_method.dsa_mod_exp = NULL;
+       if (dsa_default->bn_mod_exp == NULL)
+               dsae_method.bn_mod_exp = NULL;
+       if (dsa_default->dsa_paramgen == NULL)
+               dsae_method.dsa_paramgen = NULL;
+       if (dsa_default->dsa_keygen == NULL)
+               dsae_method.dsa_keygen = NULL;
+       dsae_method.flags = dsa_default->flags |
+           ~(DSA_FLAG_CACHE_MONT_P);
+       dsae_method.app_data = dsa_default->app_data;
+
+       if (!ENGINE_set_DSA(e, &dsae_method)) {
+               errstr = "ENGINE_set_DSA";
+               goto fail;
+       }
+       if (!ENGINE_set_default_DSA(e)) {
+               errstr = "ENGINE_set_default_DSA";
+               goto fail;
+       }
+
+       /* XXX copy-paste from above, must be a better way */
+
+       if ((e = ENGINE_get_default_ECDSA()) == NULL) {
+               if ((e = ENGINE_new()) == NULL) {
+                       errstr = "ENGINE_new";
+                       goto fail;
+               }
+               if (!ENGINE_set_name(e, ecdsae_method.name)) {
+                       errstr = "ENGINE_set_name";
+                       goto fail;
+               }
+               if ((ecdsa_default = ECDSA_get_default_method()) == NULL) {
+                       errstr = "ECDSA_get_default_method";
+                       goto fail;
+               }
+       } else if ((ecdsa_default = ENGINE_get_ECDSA(e)) == NULL) {
+               errstr = "ENGINE_get_ECDSA";
+               goto fail;
+       }
+
+       if ((name = ENGINE_get_name(e)) == NULL)
+               name = "unknown ECDSA engine";
+
+       log_debug("%s: using %s", __func__, name);
+
+       ecdsae_method.app_data = ecdsa_default->app_data;
+
+       if (!ENGINE_set_ECDSA(e, &ecdsae_method)) {
+               errstr = "ENGINE_set_ECDSA";
+               goto fail;
+       }
+       if (!ENGINE_set_default_ECDSA(e)) {
+               errstr = "ENGINE_set_default_ECDSA";
                goto fail;
        }
 
Index: relay.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.179
diff -u -p -u -r1.179 relay.c
--- relay.c     25 Oct 2014 03:23:49 -0000      1.179
+++ relay.c     8 Nov 2014 18:26:21 -0000
@@ -42,6 +42,7 @@
 #include <event.h>
 #include <fnmatch.h>
 
+#include <openssl/err.h>
 #include <openssl/dh.h>
 #include <openssl/ssl.h>
 
@@ -2038,6 +2039,18 @@ relay_ssl_ctx_create(struct relay *rlay)
            rlay->rl_ssl_cert, rlay->rl_conf.ssl_cert_len,
            &rlay->rl_ssl_x509, &rlay->rl_ssl_pkey))
                goto err;
+
+       /*
+        * All of LibReSSL DSS cipher suites require DHE.
+        * If DHE is disablid, fail early and loudly.
+        * XXX - kludgy. static.
+        */
+       if ((rlay->rl_ssl_pkey->save_type) == EVP_PKEY_DSA &&
+           (proto->ssldhparams <= 0)) {
+               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY,
+                   SSL_R_UNABLE_TO_FIND_DH_PARAMETERS);
+               goto err;
+       }
 
        if (!SSL_CTX_check_private_key(ctx))
                goto err;
Index: relayd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.conf.5,v
retrieving revision 1.151
diff -u -p -u -r1.151 relayd.conf.5
--- relayd.conf.5       21 Oct 2014 02:29:54 -0000      1.151
+++ relayd.conf.5       8 Nov 2014 18:26:26 -0000
@@ -675,7 +675,14 @@ and
 .Pa /etc/ssl/address.crt .
 See
 .Xr ssl 8
-for details about SSL server certificates.
+for details about SSL server certificates. Note that all of LibreSSL
+ciphers require you to use EDH with DSA certificates, so you must enable 
+the
+.Ic edh
+ssl option if you wish to use such certificates; see the
+.Sx PROTOCOLS
+section below.
+
 .It Ic protocol Ar name
 Use the specified protocol definition for the relay.
 The generic TCP protocol options will be used by default;
Index: relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.195
diff -u -p -u -r1.195 relayd.h
--- relayd.h    2 Nov 2014 13:59:40 -0000       1.195
+++ relayd.h    8 Nov 2014 18:26:26 -0000
@@ -233,6 +233,8 @@ struct ctl_keyop {
        int                      cko_flen;
        int                      cko_tlen;
        int                      cko_padding;
+       int                      cko_klen;
+       int                      cko_rlen;
 };
 
 struct ctl_stats {
@@ -936,7 +938,9 @@ enum imsg_type {
        IMSG_CFG_RELAY_TABLE,
        IMSG_CFG_DONE,
        IMSG_CA_PRIVENC,
-       IMSG_CA_PRIVDEC
+       IMSG_CA_PRIVDEC,
+       IMSG_CA_DSA_SIGN,
+       IMSG_CA_ECDSA_SIGN
 };
 
 enum privsep_procid {
Index: ssl.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/ssl.c,v
retrieving revision 1.25
diff -u -p -u -r1.25 ssl.c
--- ssl.c       10 Jul 2014 12:50:05 -0000      1.25
+++ ssl.c       8 Nov 2014 18:26:26 -0000
@@ -36,6 +36,8 @@
 #include <openssl/err.h>
 #include <openssl/engine.h>
 #include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/ecdsa.h>
 
 #include "relayd.h"
 
@@ -413,6 +415,8 @@ ssl_load_pkey(const void *data, size_t d
        X509            *x509 = NULL;
        EVP_PKEY        *pkey = NULL;
        RSA             *rsa = NULL;
+       DSA             *dsa = NULL;
+       EC_KEY          *ecdsa = NULL;
        void            *exdata = NULL;
 
        if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
@@ -434,15 +438,48 @@ ssl_load_pkey(const void *data, size_t d
        BIO_free(in);
 
        if (data != NULL && datalen) {
-               if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL ||
-                   (exdata = malloc(datalen)) == NULL) {
-                       SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_EVP_LIB);
-                       goto fail;
-               }
+               switch (pkey->save_type) {
+               case EVP_PKEY_RSA:      
+                       if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL ||
+                           (exdata = malloc(datalen)) == NULL) {
+                               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY,
+                                   ERR_R_EVP_LIB);
+                               goto fail;
+                       }
+                       memcpy(exdata, data, datalen);
+                       if (RSA_set_ex_data(rsa, 0, exdata) == 0)
+                               goto fail;
+                       /* dereference, will be cleaned up with pkey */
+                       RSA_free(rsa);
+                       break;
+               case EVP_PKEY_DSA:
+                       if ((dsa = EVP_PKEY_get1_DSA(pkey)) == NULL ||
+                           (exdata = malloc(datalen)) == NULL) {
+                               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY,
+                                   ERR_R_EVP_LIB);
+                               goto fail;
+                       }
+                       memcpy(exdata, data, datalen);
+                       if (DSA_set_ex_data(dsa, 0, exdata) == 0)
+                               goto fail;
+                       /* dereference, will be cleaned up with pkey */
+                       DSA_free(dsa);
+                       break;
+               case EVP_PKEY_EC:
+                       if ((ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL ||
+                           (exdata = malloc(datalen)) == NULL) {
+                               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY,
+                                   ERR_R_EVP_LIB);
+                               goto fail;
+                       }
+                       memcpy(exdata, data, datalen);
+                       if (ECDSA_set_ex_data(ecdsa, 0, exdata) == 0)
+                               goto fail;
 
-               memcpy(exdata, data, datalen);
-               RSA_set_ex_data(rsa, 0, exdata);
-               RSA_free(rsa); /* dereference, will be cleaned up with pkey */
+                       /* dereference, will be cleaned up with pkey */
+                       EC_KEY_free(ecdsa);
+                       break;
+               }
        }
 
        *x509ptr = x509;
@@ -453,6 +490,10 @@ ssl_load_pkey(const void *data, size_t d
  fail:
        if (rsa != NULL)
                RSA_free(rsa);
+       if (dsa != NULL)
+               DSA_free(dsa);
+       if (ecdsa != NULL)
+               EC_KEY_free(ecdsa);
        if (in != NULL)
                BIO_free(in);
        if (pkey != NULL)

Reply via email to