Make signedpath authdata visible via GSS naming exts
authorGreg Hudson <ghudson@mit.edu>
Sun, 23 May 2010 20:23:31 +0000 (20:23 +0000)
committerGreg Hudson <ghudson@mit.edu>
Sun, 23 May 2010 20:23:31 +0000 (20:23 +0000)
Merge users/lhoward/signedpath-naming-exts to trunk.  Adds an authdata
provider which makes non-PAC S4U2Proxy signedpath authdata visible to
application servers via GSS naming extensions.

ticket: 6733

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24094 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/krb5/krb/Makefile.in
src/lib/krb5/krb/authdata.c
src/lib/krb5/krb/authdata.h
src/lib/krb5/krb/pac.c
src/lib/krb5/krb/s4u_authdata.c [new file with mode: 0644]

index ba99032090c10303bcfc4a179bcd9d46a0e12bb8..3b3dcd74db351ab2450d47ff5f48e32b4ddb97c9 100644 (file)
@@ -86,6 +86,7 @@ STLIBOBJS= \
        rd_req_dec.o    \
        rd_safe.o       \
        recvauth.o      \
+       s4u_authdata.o  \
        s4u_creds.o     \
        sendauth.o      \
        send_tgs.o      \
@@ -184,6 +185,7 @@ OBJS=       $(OUTPRE)addr_comp.$(OBJEXT)    \
        $(OUTPRE)rd_req_dec.$(OBJEXT)   \
        $(OUTPRE)rd_safe.$(OBJEXT)      \
        $(OUTPRE)recvauth.$(OBJEXT)     \
+       $(OUTPRE)s4u_authdata.$(OBJEXT) \
        $(OUTPRE)s4u_creds.$(OBJEXT)    \
        $(OUTPRE)sendauth.$(OBJEXT)     \
        $(OUTPRE)send_tgs.$(OBJEXT)     \
@@ -283,6 +285,7 @@ SRCS=       $(srcdir)/addr_comp.c   \
        $(srcdir)/rd_req_dec.c  \
        $(srcdir)/rd_safe.c     \
        $(srcdir)/recvauth.c    \
+       $(srcdir)/s4u_authdata.c\
        $(srcdir)/s4u_creds.c   \
        $(srcdir)/sendauth.c    \
        $(srcdir)/send_tgs.c    \
index d98bfe943591005a5a5ab4f93314f72087adcaf2..3664b66d08a4c1ac51305387cc2a84628624d841 100644 (file)
@@ -44,6 +44,7 @@ static const char *objdirs[] = {
 /* Internal authdata systems */
 static krb5plugin_authdata_client_ftable_v0 *authdata_systems[] = {
     &krb5int_mspac_authdata_client_ftable,
+    &krb5int_s4u2proxy_authdata_client_ftable,
     NULL
 };
 
index 090ce336aa30789b96fef8567330e06d7daf583b..af5016ebb98c1ef1256cca8177417c4f339f1c35 100644 (file)
@@ -88,6 +88,7 @@ struct krb5_pac_data {
 #define NT_TIME_EPOCH               11644473600LL
 
 extern krb5plugin_authdata_client_ftable_v0 krb5int_mspac_authdata_client_ftable;
+extern krb5plugin_authdata_client_ftable_v0 krb5int_s4u2proxy_authdata_client_ftable;
 
 krb5_error_code
 k5_pac_locate_buffer(krb5_context context,
index b3be61c84dc6c50beca132c4700c7418a4025d21..5b6e182889604fe40e087f7a6f1072da8977fd7f 100644 (file)
@@ -1053,10 +1053,10 @@ mspac_export_internal(krb5_context kcontext,
     *ptr = NULL;
 
     if (pacctx->pac == NULL)
-        return 0;
+        return ENOENT;
 
     if (restrict_authenticated && (pacctx->pac->verified) == FALSE)
-        return 0;
+        return ENOENT;
 
     code = krb5_pac_parse(kcontext, pacctx->pac->data.data,
                           pacctx->pac->data.length, &pac);
diff --git a/src/lib/krb5/krb/s4u_authdata.c b/src/lib/krb5/krb/s4u_authdata.c
new file mode 100644 (file)
index 0000000..3c9ccb0
--- /dev/null
@@ -0,0 +1,608 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2010 by the Massachusetts Institute of Technology.  All
+ * Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include "k5-int.h"
+#include "authdata.h"
+#include "auth_con.h"
+#include "int-proto.h"
+
+/*
+ * Authdata backend for processing SignedPath. Presently does not handle
+ * the equivalent information in [MS-PAC], as that would require an NDR
+ * interpreter.
+ */
+
+struct s4u2proxy_context {
+    int count;
+    krb5_principal *delegated;
+    krb5_boolean authenticated;
+};
+
+static krb5_error_code
+s4u2proxy_init(krb5_context kcontext, void **plugin_context)
+{
+    *plugin_context = NULL;
+    return 0;
+}
+
+static void
+s4u2proxy_flags(krb5_context kcontext,
+                void *plugin_context,
+                krb5_authdatatype ad_type,
+                krb5_flags *flags)
+{
+    *flags = AD_USAGE_KDC_ISSUED;
+}
+
+static void
+s4u2proxy_fini(krb5_context kcontext, void *plugin_context)
+{
+    return;
+}
+
+static krb5_error_code
+s4u2proxy_request_init(krb5_context kcontext,
+                       krb5_authdata_context context,
+                       void *plugin_context,
+                       void **request_context)
+{
+    krb5_error_code code;
+    struct s4u2proxy_context *s4uctx;
+
+    s4uctx = k5alloc(sizeof(*s4uctx), &code);
+    if (s4uctx == NULL)
+        return code;
+
+    s4uctx->count = 0;
+    s4uctx->delegated = NULL;
+    s4uctx->authenticated = FALSE;
+
+    *request_context = s4uctx;
+
+    return 0;
+}
+
+static void
+s4u2proxy_free_internal(krb5_context kcontext,
+                        krb5_authdata_context context,
+                        void *plugin_context,
+                        void *request_context,
+                        void *ptr)
+{
+    krb5_principal *delegated = (krb5_principal *)ptr;
+    int i;
+
+    if (delegated != NULL) {
+        for (i = 0; delegated[i] != NULL; i++)
+            krb5_free_principal(kcontext, delegated[i]);
+        free(delegated);
+    }
+}
+
+static krb5_error_code
+s4u2proxy_import_authdata(krb5_context kcontext,
+                          krb5_authdata_context context,
+                          void *plugin_context,
+                          void *request_context,
+                          krb5_authdata **authdata,
+                          krb5_boolean kdc_issued,
+                          krb5_const_principal kdc_issuer)
+{
+    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
+    krb5_error_code code;
+    krb5_ad_signedpath *sp;
+    krb5_data enc_sp;
+
+    enc_sp.data = (char *)authdata[0]->contents;
+    enc_sp.length = authdata[0]->length;
+
+    code = decode_krb5_ad_signedpath(&enc_sp, &sp);
+    if (code != 0)
+        return code;
+
+    s4u2proxy_free_internal(kcontext, context,
+                            plugin_context, request_context,
+                            s4uctx->delegated);
+
+    s4uctx->delegated = sp->delegated;
+    sp->delegated = NULL;
+
+    krb5_free_ad_signedpath(kcontext, sp);
+
+    s4uctx->count = 0;
+
+    if (s4uctx->delegated != NULL) {
+        for (s4uctx->count = 0; s4uctx->delegated[s4uctx->count];
+             s4uctx->count++)
+            ;
+    }
+
+    s4uctx->authenticated = FALSE;
+
+    return 0;
+}
+
+static krb5_error_code
+s4u2proxy_export_authdata(krb5_context kcontext,
+                          krb5_authdata_context context,
+                          void *plugin_context,
+                          void *request_context,
+                          krb5_flags usage,
+                          krb5_authdata ***out_authdata)
+{
+    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
+    krb5_error_code code;
+    krb5_ad_signedpath sp;
+    krb5_authdata **authdata;
+    krb5_data *data;
+
+    if (s4uctx->count == 0)
+        return 0;
+
+    memset(&sp, 0, sizeof(sp));
+    sp.delegated = s4uctx->delegated;
+
+    authdata = k5alloc(2 * sizeof(krb5_authdata *), &code);
+    if (authdata == NULL)
+        return code;
+
+    authdata[0] = k5alloc(sizeof(krb5_authdata), &code);
+    if (authdata[0] == NULL)
+        return code;
+
+    code = encode_krb5_ad_signedpath(&sp, &data);
+    if (code != 0) {
+        krb5_free_authdata(kcontext, authdata);
+        return code;
+    }
+
+    authdata[0]->magic = KV5M_AUTHDATA;
+    authdata[0]->ad_type = KRB5_AUTHDATA_SIGNTICKET;
+    authdata[0]->length = data->length;
+    authdata[0]->contents = (krb5_octet *)data->data;
+
+    authdata[1] = NULL;
+
+    free(data);
+
+    *out_authdata = authdata;
+
+    return 0; 
+}
+
+static krb5_error_code
+s4u2proxy_verify(krb5_context kcontext,
+                 krb5_authdata_context context,
+                 void *plugin_context,
+                 void *request_context,
+                 const krb5_auth_context *auth_context,
+                 const krb5_keyblock *key,
+                 const krb5_ap_req *req)
+{
+    /*
+     * XXX there is no way to verify the SignedPath without the TGS
+     * key. This means that we can never mark this as authenticated.
+     */
+
+    return 0;
+}
+
+static void
+s4u2proxy_request_fini(krb5_context kcontext,
+                       krb5_authdata_context context,
+                       void *plugin_context,
+                       void *request_context)
+{
+    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
+
+    if (s4uctx == NULL)
+        return;
+
+    s4u2proxy_free_internal(kcontext, context,
+                            plugin_context, request_context,
+                            s4uctx->delegated);
+    free(s4uctx);
+}
+
+/*
+ * Nomenclature defined to be similar to [MS-PAC] 2.9, for future
+ * interoperability
+ */
+
+#if 0
+static krb5_data s4u2proxy_proxy_target_attr = {
+    KV5M_DATA,
+    sizeof("urn:constrained-delegation:proxy-target") - 1,
+    "urn:constrained-delegation:proxy-target"
+};
+#endif
+
+static krb5_data s4u2proxy_transited_services_attr = {
+    KV5M_DATA,
+    sizeof("urn:constrained-delegation:transited-services") - 1,
+    "urn:constrained-delegation:transited-services"
+};
+
+static krb5_error_code
+s4u2proxy_get_attribute_types(krb5_context kcontext,
+                              krb5_authdata_context context,
+                              void *plugin_context,
+                              void *request_context,
+                              krb5_data **out_attrs)
+{
+    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
+    krb5_error_code code;
+    krb5_data *attrs;
+    int i = 0;
+
+    if (s4uctx->count == 0)
+        return ENOENT;
+
+    attrs = k5alloc(2 * sizeof(krb5_data), &code);
+    if (attrs == NULL)
+        goto cleanup;
+
+    code = krb5int_copy_data_contents(kcontext,
+                                      &s4u2proxy_transited_services_attr,
+                                      &attrs[i++]);
+    if (code != 0)
+        goto cleanup;
+
+    attrs[i].data = NULL;
+    attrs[i].length = 0;
+
+    *out_attrs = attrs;
+    attrs = NULL;
+
+cleanup:
+    if (attrs != NULL) {
+        for (i = 0; attrs[i].data; i++)
+            krb5_free_data_contents(kcontext, &attrs[i]);
+        free(attrs);
+    }
+
+    return 0;
+}
+
+static krb5_error_code
+s4u2proxy_get_attribute(krb5_context kcontext,
+                        krb5_authdata_context context,
+                        void *plugin_context,
+                        void *request_context,
+                        const krb5_data *attribute,
+                        krb5_boolean *authenticated,
+                        krb5_boolean *complete,
+                        krb5_data *value, 
+                        krb5_data *display_value, 
+                        int *more)
+{
+    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
+    krb5_error_code code;
+    krb5_principal principal;
+    int i;
+
+    if (display_value != NULL) {
+        display_value->data = NULL;
+        display_value->length = 0;
+    }
+
+    if (!data_eq(*attribute, s4u2proxy_transited_services_attr))
+        return ENOENT;
+
+    i = -(*more) - 1;
+    if (i < 0)
+        return EINVAL;
+    else if (i >= s4uctx->count)
+        return ENOENT;
+
+    principal = s4uctx->delegated[i];
+    assert(principal != NULL);
+
+    code = krb5_unparse_name_flags(kcontext, principal, 0, &value->data);
+    if (code != 0)
+        return code;
+
+    value->length = strlen(value->data);
+
+    if (display_value != NULL) {
+        code = krb5_unparse_name_flags(kcontext, principal,
+                                       KRB5_PRINCIPAL_UNPARSE_DISPLAY,
+                                       &display_value->data);
+        if (code != 0)
+            return code;
+
+        display_value->length = strlen(display_value->data);
+    }
+
+    i++;
+
+    if (i == s4uctx->count)
+        *more = 0;
+    else
+        *more = -(i + 1);
+
+    *authenticated = s4uctx->authenticated;
+    *complete = TRUE;
+
+    return 0;
+}
+
+static krb5_error_code
+s4u2proxy_set_attribute(krb5_context kcontext,
+                        krb5_authdata_context context,
+                        void *plugin_context,
+                        void *request_context,
+                        krb5_boolean complete,
+                        const krb5_data *attribute,
+                        const krb5_data *value)
+{
+    /* Only the KDC can set this attribute. */
+
+    return EPERM;
+}
+
+static krb5_error_code
+s4u2proxy_export_internal(krb5_context kcontext,
+                          krb5_authdata_context context,
+                          void *plugin_context,
+                          void *request_context,
+                          krb5_boolean restrict_authenticated,
+                          void **ptr)
+{
+    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
+    krb5_error_code code;
+    int i;
+    krb5_principal *delegated;
+
+    *ptr = NULL;
+
+    if (s4uctx->count == 0)
+        return ENOENT;
+
+    if (restrict_authenticated)
+        return ENOENT;
+
+    delegated = k5alloc((s4uctx->count + 1) * sizeof(krb5_principal), &code);
+    if (delegated == NULL)
+        return code;
+
+    for (i = 0; i < s4uctx->count; i++) {
+        code = krb5_copy_principal(kcontext, s4uctx->delegated[i],
+                                   &delegated[i]);
+        if (code != 0)
+            goto cleanup;
+    }
+
+    delegated[i] = NULL;
+
+    *ptr = delegated;
+    delegated = NULL;
+
+cleanup:
+    s4u2proxy_free_internal(kcontext, context,
+                            plugin_context, request_context,
+                            delegated);
+
+    return code;
+}
+
+static krb5_error_code
+s4u2proxy_size(krb5_context kcontext,
+               krb5_authdata_context context,
+               void *plugin_context,
+               void *request_context,
+               size_t *sizep)
+{
+    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
+    krb5_error_code code = 0;
+    int i;
+
+    *sizep += sizeof(krb5_int32); /* version */
+    *sizep += sizeof(krb5_int32); /* princ count */
+
+    for (i = 0; i < s4uctx->count; i++) {
+        code = krb5_size_opaque(kcontext, KV5M_PRINCIPAL,
+                                (krb5_pointer)s4uctx->delegated[i], sizep);
+        if (code != 0)
+            return code;
+    }
+                            
+    *sizep += sizeof(krb5_int32); /* authenticated flag */
+
+    return code;
+}
+
+static krb5_error_code
+s4u2proxy_externalize(krb5_context kcontext,
+                      krb5_authdata_context context,
+                      void *plugin_context,
+                      void *request_context,
+                      krb5_octet **buffer,
+                      size_t *lenremain)
+{
+    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
+    krb5_error_code code = 0;
+    size_t required = 0;
+    krb5_octet *bp;
+    size_t remain;
+    int i = 0;
+
+    bp = *buffer;
+    remain = *lenremain;
+
+    s4u2proxy_size(kcontext, context, plugin_context,
+                   request_context, &required);
+
+    if (required > remain)
+        return ENOMEM;
+
+    krb5_ser_pack_int32(1, &bp, &remain); /* version */
+    krb5_ser_pack_int32(s4uctx->count, &bp, &remain); /* princ count */
+
+    for (i = 0; i < s4uctx->count; i++) {
+        code = krb5_externalize_opaque(kcontext, KV5M_PRINCIPAL,
+                                       (krb5_pointer)s4uctx->delegated[i],
+                                       &bp, &remain);
+        if (code != 0)
+            return code;
+    }
+    
+    krb5_ser_pack_int32(s4uctx->authenticated, &bp, &remain); /* authenticated */
+
+    *buffer = bp;
+    *lenremain = remain;
+
+    return 0;
+}
+
+static krb5_error_code
+s4u2proxy_internalize(krb5_context kcontext,
+                      krb5_authdata_context context,
+                      void *plugin_context,
+                      void *request_context,
+                      krb5_octet **buffer,
+                      size_t *lenremain)
+{
+    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
+    krb5_error_code code;
+    krb5_int32 ibuf;
+    krb5_octet *bp;
+    size_t remain;
+    int count;
+    krb5_principal *delegated = NULL;
+
+    bp = *buffer;
+    remain = *lenremain;
+
+    /* version */
+    code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+    if (code != 0)
+        goto cleanup;
+
+    if (ibuf != 1) {
+        code = EINVAL;
+        goto cleanup;
+    }
+
+    /* count */
+    code = krb5_ser_unpack_int32(&count, &bp, &remain);
+    if (code != 0)
+        goto cleanup;
+
+    if (count > 65535)
+        return ERANGE; /* let's set some reasonable limits here */
+    else if (count > 0) {
+        int i;
+
+        delegated = k5alloc((count + 1) * sizeof(krb5_principal), &code);
+        if (delegated == NULL)
+            goto cleanup;
+
+        for (i = 0; i < count; i++) {
+            code = krb5_internalize_opaque(kcontext, KV5M_PRINCIPAL,
+                                           (krb5_pointer *)&delegated[i],
+                                           &bp, &remain);
+            if (code != 0)
+                goto cleanup;
+        }
+
+        delegated[i] = NULL;
+    }
+
+    code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+    if (code != 0)
+        goto cleanup;
+
+    s4u2proxy_free_internal(kcontext, context,
+                            plugin_context, request_context,
+                            s4uctx->delegated);
+
+    s4uctx->count = count;
+    s4uctx->delegated = delegated;
+    s4uctx->authenticated = (ibuf != 0);
+
+    delegated = NULL;
+
+    *buffer = bp;
+    *lenremain = remain;
+
+cleanup:
+    s4u2proxy_free_internal(kcontext, context,
+                            plugin_context, request_context,
+                            delegated);
+
+    return code;
+}
+
+static krb5_error_code
+s4u2proxy_copy(krb5_context kcontext,
+               krb5_authdata_context context,
+               void *plugin_context,
+               void *request_context,
+               void *dst_plugin_context,
+               void *dst_request_context)
+{
+    struct s4u2proxy_context *srcctx = (struct s4u2proxy_context *)request_context;
+    struct s4u2proxy_context *dstctx = (struct s4u2proxy_context *)dst_request_context;
+    krb5_error_code code;
+
+    code = s4u2proxy_export_internal(kcontext, context,
+                                     plugin_context, request_context,
+                                     FALSE, (void **)&dstctx->delegated);
+    if (code != 0)
+        return code;
+
+    dstctx->count = srcctx->count;
+    dstctx->authenticated = srcctx->authenticated;
+
+    return 0;
+}
+
+static krb5_authdatatype s4u2proxy_ad_types[] = { KRB5_AUTHDATA_SIGNTICKET, 0 };
+
+krb5plugin_authdata_client_ftable_v0 krb5int_s4u2proxy_authdata_client_ftable = {
+    "constrained-delegation",
+    s4u2proxy_ad_types,
+    s4u2proxy_init,
+    s4u2proxy_fini,
+    s4u2proxy_flags,
+    s4u2proxy_request_init,
+    s4u2proxy_request_fini,
+    s4u2proxy_get_attribute_types,
+    s4u2proxy_get_attribute,
+    s4u2proxy_set_attribute,
+    NULL, /* delete_attribute_proc */
+    s4u2proxy_export_authdata,
+    s4u2proxy_import_authdata,
+    s4u2proxy_export_internal,
+    s4u2proxy_free_internal,
+    s4u2proxy_verify,
+    s4u2proxy_size,
+    s4u2proxy_externalize,
+    s4u2proxy_internalize,
+    s4u2proxy_copy
+};
+