Support EAP-TLS in Moonshot (requires OpenSSL)
authorLuke Howard <lukeh@padl.com>
Thu, 17 Nov 2011 08:33:22 +0000 (19:33 +1100)
committerLuke Howard <lukeh@padl.com>
Thu, 17 Nov 2011 08:33:48 +0000 (19:33 +1100)
moonshot/acinclude.m4
moonshot/configure.ac
moonshot/libeap
moonshot/mech_eap/Makefile.am
moonshot/mech_eap/gssapiP_eap.h
moonshot/mech_eap/gssapi_eap.h
moonshot/mech_eap/init_sec_context.c
moonshot/mech_eap/set_cred_option.c
moonshot/mech_eap/util.h
moonshot/mech_eap/util_cred.c

index ff06090..401ad2a 100644 (file)
@@ -115,7 +115,7 @@ else
 -DEAP_SERVER_GPSK \
 -DEAP_SERVER_GPSK_SHA256 \
 -DIEEE8021X_EAPOL";
-       EAP_LIBS="-leap -lutils -lcrypto -ltls";
+       EAP_LIBS="-leap -lutils -lcrypto -ltls -lssl";
        EAP_LDFLAGS="-L$eapdir/eap_example -L$eapdir/src/utils -L$eapdir/src/crypto -L$eapdir/src/tls";
        AC_SUBST(EAP_CFLAGS)
        AC_SUBST(EAP_LDFLAGS)
@@ -249,6 +249,44 @@ fi
 fi
 ])dnl
 
+AC_DEFUN([AX_CHECK_OPENSSL],
+[AC_MSG_CHECKING(for OpenSSL)
+OPENSSL_DIR=
+found_openssl="no"
+AC_ARG_WITH(openssl,
+    AC_HELP_STRING([--with-openssl],
+       [Use OpenSSL (in specified installation directory)]),
+    [check_openssl_dir="$withval"],
+    [check_openssl_dir=])
+for dir in $check_openssl_dir $prefix /usr /usr/local ; do
+   openssldir="$dir"
+   if test -f "$dir/include/openssl/opensslv.h"; then
+     found_openssl="yes";
+     OPENSSL_DIR="${openssldir}"
+     OPENSSL_CFLAGS="-I$openssldir/include";
+     break;
+   fi
+done
+AC_MSG_RESULT($found_openssl)
+if test x_$found_openssl != x_yes; then
+   AC_MSG_ERROR([
+----------------------------------------------------------------------
+  Cannot find OpenSSL libraries.
+
+  Please install libssl or specify installation directory with
+  --with-openssl=(dir).
+----------------------------------------------------------------------
+])
+else
+       printf "OpenSSL found in $openssldir\n";
+       OPENSSL_LIBS="-lssl -lcrypto";
+       OPENSSL_LDFLAGS="-L$openssldir/lib";
+       AC_SUBST(OPENSSL_CFLAGS)
+       AC_SUBST(OPENSSL_LDFLAGS)
+       AC_SUBST(OPENSSL_LIBS)
+fi
+])dnl
+
 AC_DEFUN([AX_CHECK_RADSEC],
 [AC_MSG_CHECKING(for radsec)
 RADSEC_DIR=
index 4297345..1049dd7 100644 (file)
@@ -81,6 +81,8 @@ if test x_$found_shibresolver = x_yes; then
   AX_CHECK_SHIBSP
 fi
 
+AX_CHECK_OPENSSL
+
 if test "x$acceptor" = "xyes" ; then
   AX_CHECK_RADSEC
   AX_CHECK_JANSSON
index 3c68005..06ba9ae 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 3c6800594dbfcba5d36cfa5556288eae999f83ba
+Subproject commit 06ba9ae7494306585ea8115619e98266ae4dc26c
index 8c6f64c..a652182 100644 (file)
@@ -42,13 +42,14 @@ mech_eap_la_CXXFLAGS += \
                        @TARGET_CFLAGS@ $(EAP_CFLAGS)
 mech_eap_la_LDFLAGS  = -avoid-version -module \
                        -export-symbols $(GSSEAP_EXPORTS) -no-undefined \
-                       @RADSEC_LDFLAGS@ @TARGET_LDFLAGS@
+                       @RADSEC_LDFLAGS@ @OPENSSL_LDFLAGS@ @TARGET_LDFLAGS@
 if TARGET_WINDOWS
 mech_eap_la_LDFLAGS += -debug
 endif
 
 mech_eap_la_LIBADD   = @KRB5_LIBS@ ../libeap/libeap.la @RADSEC_LIBS@ \
-                      @OPENSAML_LIBS@ @SHIBRESOLVER_LIBS@ @SHIBSP_LIBS@ @JANSSON_LIBS@
+                      @OPENSAML_LIBS@ @SHIBRESOLVER_LIBS@ @SHIBSP_LIBS@ @JANSSON_LIBS@ \
+                      @OPENSSL_LIBS@
 mech_eap_la_SOURCES =                          \
        acquire_cred.c                          \
        acquire_cred_with_password.c            \
index d1790a0..c763fbd 100644 (file)
@@ -150,6 +150,7 @@ struct gss_name_struct
 #define CRED_FLAG_DEFAULT_CCACHE            0x00080000
 #define CRED_FLAG_RESOLVED                  0x00100000
 #define CRED_FLAG_TARGET                    0x00200000
+#define CRED_FLAG_CERTIFICATE               0x00400000
 #define CRED_FLAG_PUBLIC_MASK               0x0000FFFF
 
 #ifdef HAVE_HEIMDAL_VERSION
@@ -170,6 +171,8 @@ struct gss_cred_id_struct
     gss_buffer_desc caCertificate;
     gss_buffer_desc subjectNameConstraint;
     gss_buffer_desc subjectAltNameConstraint;
+    gss_buffer_desc clientCertificate;
+    gss_buffer_desc privateKey;
 #ifdef GSSEAP_ENABLE_REAUTH
     krb5_ccache krbCredCache;
     gss_cred_id_t reauthCred;
index 588665b..02f132f 100644 (file)
@@ -78,6 +78,13 @@ extern gss_OID GSS_EAP_CRED_SET_CRED_FLAG;
 extern gss_OID GSS_EAP_CRED_SET_CRED_PASSWORD;
 
 /*
+ * Path to PKCS#12 private key file for use with EAP-TLS
+ * authentication.
+ */
+extern gss_OID GSS_EAP_CRED_SET_CRED_PRIVATE_KEY;
+
+
+/*
  * Credentials flag indicating the local attributes
  * processing should be skipped.
  */
index e99b479..8a877fd 100644 (file)
@@ -250,14 +250,22 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
     eapPeerConfig->anonymous_identity_len = 1 + realm.length;
 
     /* password */
-    eapPeerConfig->password = (unsigned char *)cred->password.value;
-    eapPeerConfig->password_len = cred->password.length;
+    if ((cred->flags & CRED_FLAG_CERTIFICATE) == 0) {
+        eapPeerConfig->password = (unsigned char *)cred->password.value;
+        eapPeerConfig->password_len = cred->password.length;
+    }
 
     /* certs */
     eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
     eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
     eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
 
+    if (cred->flags & CRED_FLAG_CERTIFICATE) {
+        eapPeerConfig->client_cert = (unsigned char *)cred->clientCertificate.value;
+        eapPeerConfig->private_key = (unsigned char *)cred->privateKey.value;
+        eapPeerConfig->private_key_passwd = (unsigned char *)cred->password.value;
+    }
+
     *minor = 0;
     return GSS_S_COMPLETE;
 }
index 7bb9b7b..98bb482 100644 (file)
@@ -121,6 +121,15 @@ setCredPassword(OM_uint32 *minor,
     return gssEapSetCredPassword(minor, cred, buffer);
 }
 
+static OM_uint32
+setCredPrivateKey(OM_uint32 *minor,
+                  gss_cred_id_t cred,
+                  const gss_OID oid GSSEAP_UNUSED,
+                  const gss_buffer_t buffer)
+{
+    return gssEapSetCredClientCertificate(minor, cred, GSS_C_NO_BUFFER, buffer);
+}
+
 static struct {
     gss_OID_desc oid;
     OM_uint32 (*setOption)(OM_uint32 *, gss_cred_id_t cred,
@@ -146,12 +155,18 @@ static struct {
         { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x03\x03\x04" },
         setCredPassword,
     },
+    /* 1.3.6.1.4.1.5322.22.3.3.5 */
+    {
+        { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x03\x03\x05" },
+        setCredPrivateKey,
+    },
 };
 
 gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE     = &setCredOps[0].oid;
 gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_STANZA   = &setCredOps[1].oid;
 gss_OID GSS_EAP_CRED_SET_CRED_FLAG              = &setCredOps[2].oid;
 gss_OID GSS_EAP_CRED_SET_CRED_PASSWORD          = &setCredOps[3].oid;
+gss_OID GSS_EAP_CRED_SET_CRED_PRIVATE_KEY       = &setCredOps[4].oid;
 
 OM_uint32 GSSAPI_CALLCONV
 gssspi_set_cred_option(OM_uint32 *minor,
index 4f54d41..7a6c094 100644 (file)
@@ -270,6 +270,12 @@ gssEapSetCredPassword(OM_uint32 *minor,
                       const gss_buffer_t password);
 
 OM_uint32
+gssEapSetCredClientCertificate(OM_uint32 *minor,
+                               gss_cred_id_t cred,
+                               const gss_buffer_t clientCert,
+                               const gss_buffer_t privateKey);
+
+OM_uint32
 gssEapSetCredService(OM_uint32 *minor,
                      gss_cred_id_t cred,
                      const gss_name_t target);
index 746bd61..53a19a7 100644 (file)
@@ -104,6 +104,8 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
     gss_release_buffer(&tmpMinor, &cred->caCertificate);
     gss_release_buffer(&tmpMinor, &cred->subjectNameConstraint);
     gss_release_buffer(&tmpMinor, &cred->subjectAltNameConstraint);
+    gss_release_buffer(&tmpMinor, &cred->privateKey);
+    gss_release_buffer(&tmpMinor, &cred->clientCertificate);
 
 #ifdef GSSEAP_ENABLE_REAUTH
     if (cred->krbCredCache != NULL) {
@@ -128,7 +130,8 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
 static OM_uint32
 readStaticIdentityFile(OM_uint32 *minor,
                        gss_buffer_t defaultIdentity,
-                       gss_buffer_t defaultPassword)
+                       gss_buffer_t defaultPassword,
+                       gss_buffer_t defaultPrivateKey)
 {
     OM_uint32 major, tmpMinor;
     FILE *fp = NULL;
@@ -148,6 +151,11 @@ readStaticIdentityFile(OM_uint32 *minor,
         defaultPassword->value = NULL;
     }
 
+    if (defaultPrivateKey != GSS_C_NO_BUFFER) {
+        defaultPrivateKey->length = 0;
+        defaultPrivateKey->value = NULL;
+    }
+
     ccacheName = getenv("GSSEAP_IDENTITY");
     if (ccacheName == NULL) {
 #ifdef WIN32
@@ -203,6 +211,8 @@ readStaticIdentityFile(OM_uint32 *minor,
             dst = defaultIdentity;
         else if (i == 1)
             dst = defaultPassword;
+        else if (i == 2)
+            dst = defaultPrivateKey;
         else
             break;
 
@@ -231,6 +241,7 @@ cleanup:
     if (GSS_ERROR(major)) {
         gss_release_buffer(&tmpMinor, defaultIdentity);
         zeroAndReleasePassword(defaultPassword);
+        gss_release_buffer(&tmpMinor, defaultPrivateKey);
     }
 
     memset(buf, 0, sizeof(buf));
@@ -371,7 +382,8 @@ staticIdentityFileResolveDefaultIdentity(OM_uint32 *minor,
 
     *pName = GSS_C_NO_NAME;
 
-    major = readStaticIdentityFile(minor, &defaultIdentity, GSS_C_NO_BUFFER);
+    major = readStaticIdentityFile(minor, &defaultIdentity,
+                                   GSS_C_NO_BUFFER, GSS_C_NO_BUFFER);
     if (major == GSS_S_COMPLETE) {
         major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
                                  nameMech, pName);
@@ -535,6 +547,68 @@ cleanup:
     return major;
 }
 
+/*
+ * Currently only the privateKey path is exposed to the application
+ * (via gss_set_cred_option() or the third line in ~/.gss_eap_id).
+ * At some point in the future we may add support for setting the
+ * client certificate separately.
+ */
+OM_uint32
+gssEapSetCredClientCertificate(OM_uint32 *minor,
+                              gss_cred_id_t cred,
+                              const gss_buffer_t clientCert,
+                              const gss_buffer_t privateKey)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc newClientCert = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc newPrivateKey = GSS_C_EMPTY_BUFFER;
+
+    if (cred->flags & CRED_FLAG_RESOLVED) {
+        major = GSS_S_FAILURE;
+        *minor = GSSEAP_CRED_RESOLVED;
+        goto cleanup;
+    }
+
+    if (clientCert == GSS_C_NO_BUFFER &&
+        privateKey == GSS_C_NO_BUFFER) {
+        cred->flags &= ~(CRED_FLAG_CERTIFICATE);
+        major = GSS_S_COMPLETE;
+        *minor = 0;
+        goto cleanup;
+    }
+
+    if (clientCert != GSS_C_NO_BUFFER) {
+        major = duplicateBuffer(minor, clientCert, &newClientCert);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+    if (privateKey != GSS_C_NO_BUFFER) {
+        major = duplicateBuffer(minor, privateKey, &newPrivateKey);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+    cred->flags |= CRED_FLAG_CERTIFICATE;
+
+    gss_release_buffer(&tmpMinor, &cred->clientCertificate);
+    cred->clientCertificate = newClientCert;
+
+    gss_release_buffer(&tmpMinor, &cred->privateKey);
+    cred->privateKey = newPrivateKey;
+
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
+cleanup:
+    if (GSS_ERROR(major)) {
+        gss_release_buffer(&tmpMinor, &newClientCert);
+        gss_release_buffer(&tmpMinor, &newPrivateKey);
+    }
+
+    return major;
+}
+
 OM_uint32
 gssEapSetCredService(OM_uint32 *minor,
                      gss_cred_id_t cred,
@@ -619,6 +693,8 @@ gssEapDuplicateCred(OM_uint32 *minor,
         duplicateBufferOrCleanup(&src->subjectNameConstraint, &dst->subjectNameConstraint);
     if (src->subjectAltNameConstraint.value != NULL)
         duplicateBufferOrCleanup(&src->subjectAltNameConstraint, &dst->subjectAltNameConstraint);
+    if (src->privateKey.value != NULL)
+        duplicateBufferOrCleanup(&src->privateKey, &dst->privateKey);
 
 #ifdef GSSEAP_ENABLE_REAUTH
     /* XXX krbCredCache, reauthCred */
@@ -643,9 +719,11 @@ staticIdentityFileResolveInitiatorCred(OM_uint32 *minor, gss_cred_id_t cred)
     gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
     gss_name_t defaultIdentityName = GSS_C_NO_NAME;
     gss_buffer_desc defaultPassword = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc defaultPrivateKey = GSS_C_EMPTY_BUFFER;
     int isDefaultIdentity = FALSE;
 
-    major = readStaticIdentityFile(minor, &defaultIdentity, &defaultPassword);
+    major = readStaticIdentityFile(minor, &defaultIdentity,
+                                   &defaultPassword, &defaultPrivateKey);
     if (GSS_ERROR(major))
         goto cleanup;
 
@@ -673,17 +751,26 @@ staticIdentityFileResolveInitiatorCred(OM_uint32 *minor, gss_cred_id_t cred)
         }
     }
 
-    if (isDefaultIdentity &&
-        (cred->flags & CRED_FLAG_PASSWORD) == 0) {
-        major = gssEapSetCredPassword(minor, cred, &defaultPassword);
-        if (GSS_ERROR(major))
-            goto cleanup;
+    if (isDefaultIdentity) {
+        if (defaultPrivateKey.length != 0) {
+            major = gssEapSetCredClientCertificate(minor, cred, GSS_C_NO_BUFFER,
+                                                  &defaultPrivateKey);
+            if (GSS_ERROR(major))
+                goto cleanup;
+        }
+
+        if ((cred->flags & CRED_FLAG_PASSWORD) == 0) {
+            major = gssEapSetCredPassword(minor, cred, &defaultPassword);
+            if (GSS_ERROR(major))
+                goto cleanup;
+        }
     }
 
 cleanup:
     gssEapReleaseName(&tmpMinor, &defaultIdentityName);
     zeroAndReleasePassword(&defaultPassword);
     gss_release_buffer(&tmpMinor, &defaultIdentity);
+    gss_release_buffer(&tmpMinor, &defaultPrivateKey);
 
     return major;
 }
@@ -734,7 +821,8 @@ gssEapResolveInitiatorCred(OM_uint32 *minor,
             goto cleanup;
 
         /* If we have a caller-supplied password, the credential is resolved. */
-        if ((resolvedCred->flags & CRED_FLAG_PASSWORD) == 0) {
+        if ((resolvedCred->flags &
+             (CRED_FLAG_PASSWORD | CRED_FLAG_CERTIFICATE)) == 0) {
             major = GSS_S_CRED_UNAVAIL;
             *minor = GSSEAP_NO_DEFAULT_CRED;
             goto cleanup;