summaryrefslogtreecommitdiffstats
path: root/src/lib/krb5
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2009-10-09 18:29:34 +0000
committerGreg Hudson <ghudson@mit.edu>2009-10-09 18:29:34 +0000
commit17ffdd0e93271072369e479f440ddf85e020580a (patch)
treecdaf4944a478128a1d53d854063a7d809b7c6aae /src/lib/krb5
parent6ad74ac369b09df7d29ca8e09b0af946b4819523 (diff)
downloadkrb5-17ffdd0e93271072369e479f440ddf85e020580a.tar.gz
krb5-17ffdd0e93271072369e479f440ddf85e020580a.tar.xz
krb5-17ffdd0e93271072369e479f440ddf85e020580a.zip
Implement GSS naming extensions and authdata verification
Merge Luke's users/lhoward/authdata branch to trunk. Implements GSS naming extensions and verification of authorization data. ticket: 6572 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@22875 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib/krb5')
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.c26
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.h6
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.c19
-rw-r--r--src/lib/krb5/asn.1/krb5_decode.c11
-rw-r--r--src/lib/krb5/ccache/cc_file.c4
-rw-r--r--src/lib/krb5/ccache/ccfns.c4
-rw-r--r--src/lib/krb5/error_tables/kv5m_err.et1
-rw-r--r--src/lib/krb5/krb/Makefile.in11
-rw-r--r--src/lib/krb5/krb/auth_con.c20
-rw-r--r--src/lib/krb5/krb/auth_con.h1
-rw-r--r--src/lib/krb5/krb/authdata.c1245
-rw-r--r--src/lib/krb5/krb/authdata.h48
-rw-r--r--src/lib/krb5/krb/copy_auth.c123
-rw-r--r--src/lib/krb5/krb/gc_frm_kdc.c31
-rw-r--r--src/lib/krb5/krb/int-proto.h1
-rw-r--r--src/lib/krb5/krb/kfree.c30
-rw-r--r--src/lib/krb5/krb/mk_req_ext.c40
-rw-r--r--src/lib/krb5/krb/pac.c730
-rw-r--r--src/lib/krb5/krb/rd_req.c22
-rw-r--r--src/lib/krb5/krb/rd_req_dec.c69
-rw-r--r--src/lib/krb5/krb/s4u_creds.c2
-rw-r--r--src/lib/krb5/krb/ser_actx.c2
-rw-r--r--src/lib/krb5/krb/t_authdata.c16
-rw-r--r--src/lib/krb5/libkrb5.exports20
24 files changed, 2396 insertions, 86 deletions
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c
index fe168088e6..1a46894482 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.c
+++ b/src/lib/krb5/asn.1/asn1_k_decode.c
@@ -1720,7 +1720,31 @@ asn1_error_code asn1_decode_fast_finished_ptr
decode_ptr( krb5_fast_finished *, asn1_decode_fast_finished);
}
-
+asn1_error_code asn1_decode_ad_kdcissued
+(asn1buf *buf, krb5_ad_kdcissued *val)
+{
+ setup();
+ val->ad_checksum.contents = NULL;
+ val->i_principal = NULL;
+ val->elements = NULL;
+ {begin_structure();
+ get_field(val->ad_checksum, 0, asn1_decode_checksum);
+ if (tagnum == 1) {
+ alloc_principal(val->i_principal);
+ opt_field(val->i_principal, 1, asn1_decode_realm, 0);
+ opt_field(val->i_principal, 2, asn1_decode_principal_name, 0);
+ }
+ get_field(val->elements, 3, asn1_decode_authorization_data);
+ end_structure();
+ }
+ return 0;
+error_out:
+ krb5_free_checksum_contents(NULL, &val->ad_checksum);
+ krb5_free_principal(NULL, val->i_principal);
+ krb5_free_authdata(NULL, val->elements);
+ return retval;
+}
+
#ifndef DISABLE_PKINIT
/* PKINIT */
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.h b/src/lib/krb5/asn.1/asn1_k_decode.h
index fc62c8f4ec..f0d99dcc0d 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.h
+++ b/src/lib/krb5/asn.1/asn1_k_decode.h
@@ -282,4 +282,10 @@ asn1_error_code asn1_decode_fast_finished
asn1_error_code asn1_decode_fast_finished_ptr
(asn1buf *buf, krb5_fast_finished **val);
+asn1_error_code asn1_decode_ad_kdcissued
+(asn1buf *buf, krb5_ad_kdcissued *val);
+
+asn1_error_code asn1_decode_ad_kdcissued_ptr
+(asn1buf *buf, krb5_ad_kdcissued **val);
+
#endif
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index cd63ffbb95..1e9f11fe8c 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -1290,6 +1290,23 @@ DEFSEQTYPE(fast_rep, krb5_enc_data, fast_rep_fields, 0);
DEFFIELDTYPE(pa_fx_fast_reply, krb5_enc_data,
FIELDOF_ENCODEAS(krb5_enc_data, fast_rep, 0));
+static const struct field_info ad_kdcissued_fields[] = {
+ FIELDOF_NORM(krb5_ad_kdcissued, checksum, ad_checksum, 0),
+ FIELDOF_OPT(krb5_ad_kdcissued, realm_of_principal, i_principal, 1, 1),
+ FIELDOF_OPT(krb5_ad_kdcissued, principal, i_principal, 2, 1),
+ FIELDOF_NORM(krb5_ad_kdcissued, auth_data_ptr, elements, 3),
+};
+
+static unsigned int ad_kdcissued_optional(const void *p)
+{
+ unsigned int optional = 0;
+ const krb5_ad_kdcissued *val = p;
+ if (val->i_principal)
+ optional |= (1u << 1);
+ return optional;
+}
+
+DEFSEQTYPE(ad_kdc_issued, krb5_ad_kdcissued, ad_kdcissued_fields, ad_kdcissued_optional);
@@ -1366,7 +1383,7 @@ MAKE_FULL_ENCODER( encode_krb5_fast_req, fast_req);
MAKE_FULL_ENCODER( encode_krb5_pa_fx_fast_reply, pa_fx_fast_reply);
MAKE_FULL_ENCODER(encode_krb5_fast_response, fast_response);
-
+MAKE_FULL_ENCODER(encode_krb5_ad_kdcissued, ad_kdc_issued);
diff --git a/src/lib/krb5/asn.1/krb5_decode.c b/src/lib/krb5/asn.1/krb5_decode.c
index a2e9c0a4dd..215608d33a 100644
--- a/src/lib/krb5/asn.1/krb5_decode.c
+++ b/src/lib/krb5/asn.1/krb5_decode.c
@@ -1180,6 +1180,17 @@ krb5_error_code decode_krb5_pa_fx_fast_reply
cleanup(free);
}
+krb5_error_code decode_krb5_ad_kdcissued
+(const krb5_data *code, krb5_ad_kdcissued **repptr)
+{
+ setup_buf_only(krb5_ad_kdcissued *);
+ alloc_field(rep);
+
+ retval = asn1_decode_ad_kdcissued(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
#ifndef DISABLE_PKINIT
krb5_error_code
diff --git a/src/lib/krb5/ccache/cc_file.c b/src/lib/krb5/ccache/cc_file.c
index bd93fa4315..32564a04eb 100644
--- a/src/lib/krb5/ccache/cc_file.c
+++ b/src/lib/krb5/ccache/cc_file.c
@@ -859,14 +859,14 @@ krb5_fcc_read_authdatum(krb5_context context, krb5_ccache id, krb5_authdata *a)
{
krb5_error_code kret;
krb5_int32 int32;
- krb5_ui_2 ui2;
+ krb5_int16 ui2; /* negative authorization data types are allowed */
k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
a->magic = KV5M_AUTHDATA;
a->contents = NULL;
- kret = krb5_fcc_read_ui_2(context, id, &ui2);
+ kret = krb5_fcc_read_ui_2(context, id, (krb5_ui_2 *)&ui2);
CHECK(kret);
a->ad_type = (krb5_authdatatype)ui2;
kret = krb5_fcc_read_int32(context, id, &int32);
diff --git a/src/lib/krb5/ccache/ccfns.c b/src/lib/krb5/ccache/ccfns.c
index 5d95a64289..abfc037be3 100644
--- a/src/lib/krb5/ccache/ccfns.c
+++ b/src/lib/krb5/ccache/ccfns.c
@@ -70,7 +70,7 @@ krb5_cc_store_cred (krb5_context context, krb5_ccache cache,
krb5_principal s1, s2;
/* remove any dups */
- krb5_cc_remove_cred(context, cache, 0, creds);
+ krb5_cc_remove_cred(context, cache, KRB5_TC_MATCH_AUTHDATA, creds);
ret = cache->ops->store(context, cache, creds);
if (ret) return ret;
@@ -87,7 +87,7 @@ krb5_cc_store_cred (krb5_context context, krb5_ccache cache,
if (!krb5_principal_compare(context, s1, s2)) {
creds->server = s2;
/* remove any dups */
- krb5_cc_remove_cred(context, cache, 0, creds);
+ krb5_cc_remove_cred(context, cache, KRB5_TC_MATCH_AUTHDATA, creds);
ret = cache->ops->store(context, cache, creds);
creds->server = s1;
}
diff --git a/src/lib/krb5/error_tables/kv5m_err.et b/src/lib/krb5/error_tables/kv5m_err.et
index d68398cf97..6259adab7c 100644
--- a/src/lib/krb5/error_tables/kv5m_err.et
+++ b/src/lib/krb5/error_tables/kv5m_err.et
@@ -89,4 +89,5 @@ error_code KV5M_GSS_QUEUE, "Bad magic number for GSSAPI QUEUE"
error_code KV5M_FAST_ARMORED_REQ, "Bad magic number for fast armored request"
error_code KV5M_FAST_REQ, "Bad magic number for FAST request"
error_code KV5M_FAST_RESPONSE, "Bad magic number for FAST response"
+error_code KV5M_AUTHDATA_CONTEXT, "Bad magic number for krb5_authdata_context"
end
diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index 8b8f6d2db0..3746746fe3 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -18,6 +18,7 @@ STLIBOBJS= \
addr_srch.o \
appdefault.o \
auth_con.o \
+ authdata.o \
bld_pr_ext.o \
bld_princ.o \
chk_trans.o \
@@ -107,6 +108,7 @@ OBJS= $(OUTPRE)addr_comp.$(OBJEXT) \
$(OUTPRE)addr_srch.$(OBJEXT) \
$(OUTPRE)appdefault.$(OBJEXT) \
$(OUTPRE)auth_con.$(OBJEXT) \
+ $(OUTPRE)authdata.$(OBJEXT) \
$(OUTPRE)bld_pr_ext.$(OBJEXT) \
$(OUTPRE)bld_princ.$(OBJEXT) \
$(OUTPRE)chk_trans.$(OBJEXT) \
@@ -196,6 +198,7 @@ SRCS= $(srcdir)/addr_comp.c \
$(srcdir)/addr_srch.c \
$(srcdir)/appdefault.c \
$(srcdir)/auth_con.c \
+ $(srcdir)/authdata.c \
$(srcdir)/bld_pr_ext.c \
$(srcdir)/bld_princ.c \
$(srcdir)/brand.c \
@@ -312,11 +315,11 @@ T_WALK_RTREE_OBJS= t_walk_rtree.o walk_rtree.o tgtname.o unparse.o \
T_KERB_OBJS= t_kerb.o conv_princ.o unparse.o set_realm.o str_conv.o
T_SER_OBJS= t_ser.o ser_actx.o ser_adata.o ser_addr.o ser_auth.o ser_cksum.o \
- ser_ctx.o ser_key.o ser_princ.o serialize.o
+ ser_ctx.o ser_key.o ser_princ.o serialize.o authdata.o pac.o copy_data.o
T_DELTAT_OBJS= t_deltat.o deltat.o
-T_PAC_OBJS= t_pac.o pac.o
+T_PAC_OBJS= t_pac.o pac.o copy_data.o
T_PRINC_OBJS= t_princ.o parse.o unparse.o
@@ -327,8 +330,8 @@ t_walk_rtree: $(T_WALK_RTREE_OBJS) $(KRB5_BASE_DEPLIBS)
t_ad_fx_armor: t_ad_fx_armor.o
$(CC_LINK) -o $@ t_ad_fx_armor.o $(KRB5_BASE_LIBS)
-t_authdata: t_authdata.o copy_auth.o
- $(CC_LINK) -o $@ t_authdata.o copy_auth.o $(KRB5_BASE_LIBS)
+t_authdata: t_authdata.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ t_authdata.o $(KRB5_BASE_LIBS)
t_kerb: $(T_KERB_OBJS) $(KRB5_BASE_DEPLIBS)
$(CC_LINK) -o t_kerb $(T_KERB_OBJS) $(KRB5_BASE_LIBS)
diff --git a/src/lib/krb5/krb/auth_con.c b/src/lib/krb5/krb/auth_con.c
index 41a2578e0a..b88219cdb1 100644
--- a/src/lib/krb5/krb/auth_con.c
+++ b/src/lib/krb5/krb/auth_con.c
@@ -66,6 +66,8 @@ krb5_auth_con_free(krb5_context context, krb5_auth_context auth_context)
krb5_rc_close(context, auth_context->rcache);
if (auth_context->permitted_etypes)
free(auth_context->permitted_etypes);
+ if (auth_context->ad_context)
+ krb5_authdata_context_free(context, auth_context->ad_context);
free(auth_context);
return 0;
}
@@ -568,3 +570,21 @@ krb5_auth_con_get_subkey_enctype(krb5_context context,
return 0;
}
+krb5_error_code KRB5_CALLCONV
+krb5_auth_con_get_authdata_context(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_authdata_context *ad_context)
+{
+ *ad_context = auth_context->ad_context;
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_auth_con_set_authdata_context(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_authdata_context ad_context)
+{
+ auth_context->ad_context = ad_context;
+ return 0;
+}
+
diff --git a/src/lib/krb5/krb/auth_con.h b/src/lib/krb5/krb/auth_con.h
index be63bedbf4..6254ac67c2 100644
--- a/src/lib/krb5/krb/auth_con.h
+++ b/src/lib/krb5/krb/auth_con.h
@@ -24,6 +24,7 @@ struct _krb5_auth_context {
krb5_mk_req_checksum_func checksum_func;
void *checksum_func_data;
krb5_enctype negotiated_etype;
+ krb5_authdata_context ad_context;
};
diff --git a/src/lib/krb5/krb/authdata.c b/src/lib/krb5/krb/authdata.c
new file mode 100644
index 0000000000..6e1b9b5b16
--- /dev/null
+++ b/src/lib/krb5/krb/authdata.c
@@ -0,0 +1,1245 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009 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"
+
+/* Loosely based on preauth2.c */
+
+#define IS_PRIMARY_INSTANCE(_module) ((_module)->client_req_init != NULL)
+
+static const char *objdirs[] = {
+#if TARGET_OS_MAC
+ KRB5_AUTHDATA_PLUGIN_BUNDLE_DIR,
+#endif
+ LIBDIR "/krb5/plugins/authdata",
+ NULL
+ }; /* should be a list */
+
+/* Internal authdata systems */
+static krb5plugin_authdata_client_ftable_v0 *authdata_systems[] = {
+ &krb5int_mspac_authdata_client_ftable,
+ NULL
+};
+
+static inline int
+k5_ad_module_count(krb5plugin_authdata_client_ftable_v0 *table)
+{
+ int i;
+
+ if (table->ad_type_list == NULL)
+ return 0;
+
+ for (i = 0; table->ad_type_list[i]; i++)
+ ;
+
+ return i;
+}
+
+static krb5_error_code
+k5_ad_init_modules(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5plugin_authdata_client_ftable_v0 *table,
+ int *module_count)
+{
+ int j, k = *module_count;
+ krb5_error_code code;
+ void *plugin_context = NULL;
+ void **rcpp;
+
+ if (table->ad_type_list == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "warning: module \"%s\" does not advertise "
+ "any AD types\n", table->name);
+#endif
+ return ENOENT;
+ }
+
+ if (table->init == NULL)
+ return ENOSYS;
+
+ code = (*table->init)(kcontext, &plugin_context);
+ if (code != 0) {
+#ifdef DEBUG
+ fprintf(stderr, "warning: skipping module \"%s\" which "
+ "failed to initialize\n", table->name);
+#endif
+ return code;
+ }
+
+ for (j = 0; table->ad_type_list[j] != 0; j++) {
+ context->modules[k].ad_type = table->ad_type_list[j];
+ context->modules[k].plugin_context = plugin_context;
+ if (j == 0)
+ context->modules[k].client_fini = table->fini;
+ else
+ context->modules[k].client_fini = NULL;
+ context->modules[k].ftable = table;
+ context->modules[k].name = table->name;
+ if (table->flags != NULL) {
+ (*table->flags)(kcontext, plugin_context,
+ context->modules[k].ad_type,
+ &context->modules[k].flags);
+ } else {
+ context->modules[k].flags = 0;
+ }
+ context->modules[k].request_context = NULL;
+ if (j == 0) {
+ context->modules[k].client_req_init = table->request_init;
+ context->modules[k].client_req_fini = table->request_fini;
+ rcpp = &context->modules[k].request_context;
+
+ /* For now, single request per context. That may change */
+ code = (*table->request_init)(kcontext,
+ context,
+ plugin_context,
+ rcpp);
+ if ((code != 0 && code != ENOMEM) &&
+ (context->modules[k].flags & AD_INFORMATIONAL))
+ code = 0;
+ if (code != 0)
+ break;
+ } else {
+ context->modules[k].client_req_init = NULL;
+ context->modules[k].client_req_fini = NULL;
+ }
+ context->modules[k].request_context_pp = rcpp;
+
+#ifdef DEBUG
+ fprintf(stderr, "init module \"%s\", ad_type %d, flags %08x\n",
+ context->modules[k].name,
+ context->modules[k].ad_type,
+ context->modules[k].flags);
+#endif
+ k++;
+ }
+ *module_count = k;
+
+ return code;
+}
+
+/*
+ * Determine size of to-be-externalized authdata context, for
+ * modules that match given flags mask. Note that this size
+ * does not include the magic identifier/trailer.
+ */
+static krb5_error_code
+k5_ad_size(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_flags flags,
+ size_t *sizep)
+{
+ int i;
+ krb5_error_code code = 0;
+
+ *sizep += sizeof(krb5_int32); /* count */
+
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+ size_t size;
+
+ if ((module->flags & flags) == 0)
+ continue;
+
+ /* externalize request context for the first instance only */
+ if (!IS_PRIMARY_INSTANCE(module))
+ continue;
+
+ if (module->ftable->size == NULL)
+ continue;
+
+ assert(module->ftable->externalize != NULL);
+
+ size = sizeof(krb5_int32) /* namelen */ + strlen(module->name);
+
+ code = (*module->ftable->size)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ &size);
+ if (code != 0)
+ break;
+
+ *sizep += size;
+ }
+
+ return code;
+}
+
+/*
+ * Externalize authdata context, for modules that match given flags
+ * mask. Note that the magic identifier/trailer is not included.
+ */
+static krb5_error_code
+k5_ad_externalize(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_flags flags,
+ krb5_octet **buffer,
+ size_t *lenremain)
+{
+ int i;
+ krb5_error_code code;
+ krb5_int32 ad_count = 0;
+ krb5_octet *bp;
+ size_t remain;
+
+ bp = *buffer;
+ remain = *lenremain;
+
+ /* placeholder for count */
+ code = krb5_ser_pack_int32(0, &bp, &remain);
+ if (code != 0)
+ return code;
+
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+ size_t namelen;
+
+ if ((module->flags & flags) == 0)
+ continue;
+
+ /* externalize request context for the first instance only */
+ if (!IS_PRIMARY_INSTANCE(module))
+ continue;
+
+ if (module->ftable->externalize == NULL)
+ continue;
+
+ /*
+ * We use the module name rather than the authdata type, because
+ * there may be multiple modules for a particular authdata type.
+ */
+ namelen = strlen(module->name);
+
+ code = krb5_ser_pack_int32((krb5_int32)namelen, &bp, &remain);
+ if (code != 0)
+ break;
+
+ code = krb5_ser_pack_bytes((krb5_octet *)module->name,
+ namelen, &bp, &remain);
+ if (code != 0)
+ break;
+
+ code = (*module->ftable->externalize)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ &bp,
+ &remain);
+ if (code != 0)
+ break;
+
+ ad_count++;
+ }
+
+ if (code == 0) {
+ /* store actual count */
+ krb5_ser_pack_int32(ad_count, buffer, lenremain);
+
+ *buffer = bp;
+ *lenremain = remain;
+ }
+
+ return code;
+}
+
+/*
+ * Find authdata module for authdata type that matches flag mask
+ */
+static struct _krb5_authdata_context_module *
+k5_ad_find_module(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_flags flags,
+ const krb5_data *name)
+{
+ int i;
+ struct _krb5_authdata_context_module *ret = NULL;
+
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+
+ if ((module->flags & flags) == 0)
+ continue;
+
+ /* internalize request context for the first instance only */
+ if (!IS_PRIMARY_INSTANCE(module))
+ continue;
+
+ /* check for name match */
+ if (strlen(module->name) != name->length ||
+ memcmp(module->name, name->data, name->length) != 0)
+ continue;
+
+ ret = module;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * In-place internalize authdata context, for modules that match given
+ * flags mask. The magic identifier/trailer is not expected by this.
+ */
+static krb5_error_code
+k5_ad_internalize(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_flags flags,
+ krb5_octet **buffer,
+ size_t *lenremain)
+{
+ krb5_error_code code = 0;
+ krb5_int32 i, count;
+ krb5_octet *bp;
+ size_t remain;
+
+ bp = *buffer;
+ remain = *lenremain;
+
+ code = krb5_ser_unpack_int32(&count, &bp, &remain);
+ if (code != 0)
+ return code;
+
+ for (i = 0; i < count; i++) {
+ struct _krb5_authdata_context_module *module;
+ krb5_int32 namelen;
+ krb5_data name;
+
+ code = krb5_ser_unpack_int32(&namelen, &bp, &remain);
+ if (code != 0)
+ break;
+
+ if (remain < (size_t)namelen) {
+ code = ENOMEM;
+ break;
+ }
+
+ name.length = namelen;
+ name.data = (char *)bp;
+
+ module = k5_ad_find_module(kcontext, context, flags, &name);
+ if (module == NULL || module->ftable->internalize == NULL) {
+ code = EINVAL;
+ break;
+ }
+
+ bp += namelen;
+ remain -= namelen;
+
+ code = (*module->ftable->internalize)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ &bp,
+ &remain);
+ if (code != 0)
+ break;
+ }
+
+ if (code == 0) {
+ *buffer = bp;
+ *lenremain = remain;
+ }
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_context_init(krb5_context kcontext,
+ krb5_authdata_context *pcontext)
+{
+ int n_modules, n_tables, i, k;
+ void **tables = NULL;
+ krb5plugin_authdata_client_ftable_v0 *table;
+ krb5_authdata_context context = NULL;
+ int internal_count = 0;
+ struct plugin_dir_handle plugins;
+ krb5_error_code code;
+
+ *pcontext = NULL;
+ memset(&plugins, 0, sizeof(plugins));
+
+ n_modules = 0;
+ for (n_tables = 0; authdata_systems[n_tables] != NULL; n_tables++) {
+ n_modules += k5_ad_module_count(authdata_systems[n_tables]);
+ }
+ internal_count = n_tables;
+
+ if (PLUGIN_DIR_OPEN(&plugins) == 0 &&
+ krb5int_open_plugin_dirs(objdirs, NULL,
+ &plugins,
+ &kcontext->err) == 0 &&
+ krb5int_get_plugin_dir_data(&plugins,
+ "authdata_client_0",
+ &tables,
+ &kcontext->err) == 0 &&
+ tables != NULL)
+ {
+ for (; tables[n_tables - internal_count] != NULL; n_tables++) {
+ table = tables[n_tables - internal_count];
+ n_modules += k5_ad_module_count(table);
+ }
+ }
+
+ context = calloc(1, sizeof(*context));
+ if (kcontext == NULL) {
+ if (tables != NULL)
+ krb5int_free_plugin_dir_data(tables);
+ krb5int_close_plugin_dirs(&context->plugins);
+ return ENOMEM;
+ }
+ context->magic = KV5M_AUTHDATA_CONTEXT;
+ context->modules = calloc(n_modules, sizeof(context->modules[0]));
+ if (context->modules == NULL) {
+ if (tables != NULL)
+ krb5int_free_plugin_dir_data(tables);
+ krb5int_close_plugin_dirs(&context->plugins);
+ free(kcontext);
+ return ENOMEM;
+ }
+ context->n_modules = n_modules;
+
+ /* fill in the structure */
+ for (i = 0, k = 0, code = 0; i < n_tables - internal_count; i++) {
+ code = k5_ad_init_modules(kcontext, context, tables[i], &k);
+ if (code != 0)
+ break;
+ }
+
+ if (code == 0) {
+ for (i = 0; i < internal_count; i++) {
+ code = k5_ad_init_modules(kcontext, context, authdata_systems[i], &k);
+ if (code != 0)
+ break;
+ }
+ }
+
+ if (tables != NULL)
+ krb5int_free_plugin_dir_data(tables);
+
+ context->plugins = plugins;
+
+ if (code != 0)
+ krb5_authdata_context_free(kcontext, context);
+ else
+ *pcontext = context;
+
+ return code;
+}
+
+void KRB5_CALLCONV
+krb5_authdata_context_free(krb5_context kcontext,
+ krb5_authdata_context context)
+{
+ int i;
+
+ if (context == NULL)
+ return;
+
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+
+ if (module->client_req_fini != NULL &&
+ module->request_context != NULL)
+ (*module->client_req_fini)(kcontext,
+ context,
+ module->plugin_context,
+ module->request_context);
+
+ if (module->client_fini != NULL)
+ (*module->client_fini)(kcontext, module->plugin_context);
+
+ memset(module, 0, sizeof(*module));
+ }
+
+ if (context->modules != NULL) {
+ free(context->modules);
+ context->modules = NULL;
+ }
+ krb5int_close_plugin_dirs(&context->plugins);
+ memset(context, 0, sizeof(*context));
+ free(context);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_import_attributes(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_flags usage,
+ const krb5_data *attrs)
+{
+ krb5_octet *bp;
+ size_t remain;
+
+ bp = (krb5_octet *)attrs->data;
+ remain = attrs->length;
+
+ return k5_ad_internalize(kcontext, context, usage, &bp, &remain);
+}
+
+static krb5_error_code
+k5_get_kdc_issued_authdata(krb5_context kcontext,
+ const krb5_ap_req *ap_req,
+ krb5_principal *kdc_issuer,
+ krb5_authdata ***kdc_issued_authdata)
+{
+ krb5_error_code code;
+ krb5_authdata **authdata;
+ krb5_authdata **ticket_authdata;
+
+ *kdc_issuer = NULL;
+ *kdc_issued_authdata = NULL;
+
+ ticket_authdata = ap_req->ticket->enc_part2->authorization_data;
+
+ code = krb5int_find_authdata(kcontext,
+ ticket_authdata,
+ NULL,
+ KRB5_AUTHDATA_KDC_ISSUED,
+ &authdata);
+ if (code != 0 || authdata == NULL)
+ return code;
+
+ /*
+ * Note: a module must still implement a verify_authdata
+ * method, even it is a NOOP that simply records the value
+ * of the kdc_issued_flag.
+ */
+ code = krb5_verify_authdata_kdc_issued(kcontext,
+ ap_req->ticket->enc_part2->session,
+ authdata[0],
+ kdc_issuer,
+ kdc_issued_authdata);
+
+ assert(code == 0 || *kdc_issued_authdata == NULL);
+
+ krb5_free_authdata(kcontext, authdata);
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_export_authdata(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_flags flags,
+ krb5_authdata ***pauthdata)
+{
+ int i;
+ krb5_error_code code = 0;
+ krb5_authdata **authdata = NULL;
+ unsigned int len = 0;
+
+ *pauthdata = NULL;
+
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+ krb5_authdata **authdata2 = NULL;
+ int j;
+
+ if ((module->flags & flags) == 0)
+ continue;
+
+ if (module->ftable->export_authdata == NULL)
+ continue;
+
+ code = (*module->ftable->export_authdata)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ flags,
+ &authdata2);
+ if (code == ENOENT)
+ code = 0;
+ else if (code != 0)
+ break;
+
+ if (authdata2 == NULL)
+ continue;
+
+ for (j = 0; authdata2[j] != NULL; j++)
+ ;
+
+ authdata = realloc(authdata, (len + j + 1) * sizeof(krb5_authdata *));
+ if (authdata == NULL)
+ return ENOMEM;
+
+ memcpy(&authdata[len], authdata2, j * sizeof(krb5_authdata *));
+ free(authdata2);
+
+ len += j;
+ }
+
+ if (authdata != NULL)
+ authdata[len] = NULL;
+
+ *pauthdata = authdata;
+
+ return code;
+}
+
+krb5_error_code
+krb5int_authdata_verify(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_flags usage,
+ const krb5_auth_context *auth_context,
+ const krb5_keyblock *key,
+ const krb5_ap_req *ap_req)
+{
+ int i;
+ krb5_error_code code = 0;
+ krb5_authdata **authen_authdata;
+ krb5_authdata **ticket_authdata;
+ krb5_principal kdc_issuer = NULL;
+ krb5_authdata **kdc_issued_authdata = NULL;
+
+ authen_authdata = (*auth_context)->authentp->authorization_data;
+ ticket_authdata = ap_req->ticket->enc_part2->authorization_data;
+ k5_get_kdc_issued_authdata(kcontext, ap_req,
+ &kdc_issuer, &kdc_issued_authdata);
+
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+ krb5_authdata **authdata = NULL;
+ krb5_boolean kdc_issued_flag = FALSE;
+
+ if ((module->flags & usage) == 0)
+ continue;
+
+ if (module->ftable->import_authdata == NULL)
+ continue;
+
+ if (kdc_issued_authdata != NULL) {
+ code = krb5int_find_authdata(kcontext,
+ kdc_issued_authdata,
+ NULL,
+ module->ad_type,
+ &authdata);
+ if (code != 0)
+ break;
+
+ kdc_issued_flag = TRUE;
+ }
+
+ if (authdata == NULL) {
+ code = krb5int_find_authdata(kcontext,
+ ticket_authdata,
+ authen_authdata,
+ module->ad_type,
+ &authdata);
+ if (code != 0)
+ break;
+ }
+
+ if (authdata == NULL)
+ continue;
+
+ assert(authdata[0] != NULL);
+
+ code = (*module->ftable->import_authdata)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ authdata,
+ kdc_issued_flag,
+ kdc_issuer);
+ if (code == 0 && module->ftable->verify != NULL) {
+ code = (*module->ftable->verify)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ auth_context,
+ key,
+ ap_req);
+ }
+ if (code != 0 && (module->flags & AD_INFORMATIONAL))
+ code = 0;
+ krb5_free_authdata(kcontext, authdata);
+ if (code != 0)
+ break;
+ }
+
+ krb5_free_principal(kcontext, kdc_issuer);
+ krb5_free_authdata(kcontext, kdc_issued_authdata);
+
+ return code;
+}
+
+static krb5_error_code
+k5_merge_data_list(krb5_data **dst, krb5_data *src, unsigned int *len)
+{
+ unsigned int i;
+ krb5_data *d;
+
+ if (src == NULL)
+ return 0;
+
+ for (i = 0; src[i].data != NULL; i++)
+ ;
+
+ d = realloc(*dst, (*len + i + 1) * sizeof(krb5_data));
+ if (d == NULL)
+ return ENOMEM;
+
+ memcpy(&d[*len], src, i * sizeof(krb5_data));
+
+ *len += i;
+
+ d[*len].data = NULL;
+ d[*len].length = 0;
+
+ *dst = d;
+
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_get_attribute_types(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_data **out_attrs)
+{
+ int i;
+ krb5_error_code code = 0;
+ krb5_data *attrs = NULL;
+ unsigned int attrs_len = 0;
+
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+ krb5_data *attrs2 = NULL;
+
+ if (module->ftable->get_attribute_types == NULL)
+ continue;
+
+ if ((*module->ftable->get_attribute_types)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ &attrs2))
+ continue;
+
+ code = k5_merge_data_list(&attrs, attrs2, &attrs_len);
+ if (code != 0) {
+ krb5int_free_data_list(kcontext, attrs2);
+ break;
+ }
+ if (attrs2 != NULL)
+ free(attrs2);
+ }
+
+ if (code != 0) {
+ krb5int_free_data_list(kcontext, attrs);
+ attrs = NULL;
+ }
+
+ *out_attrs = attrs;
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_get_attribute(krb5_context kcontext,
+ krb5_authdata_context context,
+ const krb5_data *attribute,
+ krb5_boolean *authenticated,
+ krb5_boolean *complete,
+ krb5_data *value,
+ krb5_data *display_value,
+ int *more)
+{
+ int i;
+ krb5_error_code code = ENOENT;
+
+ *authenticated = FALSE;
+ *complete = FALSE;
+
+ value->data = NULL;
+ value->length = 0;
+
+ display_value->data = NULL;
+ display_value->length = 0;
+
+ /*
+ * NB at present a module is presumed to be authoritative for
+ * an attribute; not sure how to federate "more" across module
+ * yet
+ */
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+
+ if (module->ftable->get_attribute == NULL)
+ continue;
+
+ code = (*module->ftable->get_attribute)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ attribute,
+ authenticated,
+ complete,
+ value,
+ display_value,
+ more);
+ if (code == 0)
+ break;
+ }
+
+ if (code != 0)
+ *more = 0;
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_set_attribute(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_boolean complete,
+ const krb5_data *attribute,
+ const krb5_data *value)
+{
+ int i;
+ krb5_error_code code = 0;
+ int found = 0;
+
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+
+ if (module->ftable->set_attribute == NULL)
+ continue;
+
+ code = (*module->ftable->set_attribute)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ complete,
+ attribute,
+ value);
+ if (code == ENOENT)
+ code = 0;
+ else if (code == 0)
+ found++;
+ else
+ break;
+ }
+
+ if (code == 0 && found == 0)
+ code = ENOENT;
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_delete_attribute(krb5_context kcontext,
+ krb5_authdata_context context,
+ const krb5_data *attribute)
+{
+ int i;
+ krb5_error_code code = ENOENT;
+ int found = 0;
+
+ for (i = 0; i < context->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &context->modules[i];
+
+ if (module->ftable->delete_attribute == NULL)
+ continue;
+
+ code = (*module->ftable->delete_attribute)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ attribute);
+ if (code == ENOENT)
+ code = 0;
+ else if (code == 0)
+ found++;
+ else
+ break;
+ }
+
+ if (code == 0 && found == 0)
+ code = ENOENT;
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_export_attributes(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_flags flags,
+ krb5_data **attrsp)
+{
+ krb5_error_code code;
+ size_t required = 0;
+ krb5_octet *bp;
+ size_t remain;
+ krb5_data *attrs;
+
+ code = k5_ad_size(kcontext, context, AD_USAGE_MASK, &required);
+ if (code != 0)
+ return code;
+
+ attrs = malloc(sizeof(*attrs));
+ if (attrs == NULL)
+ return ENOMEM;
+
+ attrs->magic = KV5M_DATA;
+ attrs->length = 0;
+ attrs->data = malloc(required);
+ if (attrs->data == NULL) {
+ free(attrs);
+ return ENOMEM;
+ }
+
+ bp = (krb5_octet *)attrs->data;
+ remain = required;
+
+ code = k5_ad_externalize(kcontext, context, AD_USAGE_MASK, &bp, &remain);
+ if (code != 0) {
+ krb5_free_data(kcontext, attrs);
+ return code;
+ }
+
+ attrs->length = (bp - (krb5_octet *)attrs->data);
+
+ *attrsp = attrs;
+
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_export_internal(krb5_context kcontext,
+ krb5_authdata_context context,
+ krb5_boolean restrict_authenticated,
+ const char *module_name,
+ void **ptr)
+{
+ krb5_error_code code;
+ krb5_data name;
+ struct _krb5_authdata_context_module *module;
+
+ *ptr = NULL;
+
+ name.length = strlen(module_name);
+ name.data = (char *)module_name;
+
+ module = k5_ad_find_module(kcontext, context, AD_USAGE_MASK, &name);
+ if (module == NULL)
+ return ENOENT;
+
+ if (module->ftable->export_internal == NULL)
+ return ENOENT;
+
+ code = (*module->ftable->export_internal)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ restrict_authenticated,
+ ptr);
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_free_internal(krb5_context kcontext,
+ krb5_authdata_context context,
+ const char *module_name,
+ void *ptr)
+{
+ krb5_data name;
+ struct _krb5_authdata_context_module *module;
+
+ name.length = strlen(module_name);
+ name.data = (char *)module_name;
+
+ module = k5_ad_find_module(kcontext, context, AD_USAGE_MASK, &name);
+ if (module == NULL)
+ return ENOENT;
+
+ if (module->ftable->free_internal == NULL)
+ return ENOENT;
+
+ (*module->ftable->free_internal)(kcontext,
+ context,
+ module->plugin_context,
+ *(module->request_context_pp),
+ ptr);
+
+ return 0;
+}
+
+static krb5_error_code
+k5_copy_ad_module_data(krb5_context kcontext,
+ krb5_authdata_context context,
+ struct _krb5_authdata_context_module *src_module,
+ krb5_authdata_context dst)
+{
+ int i;
+ krb5_error_code code;
+ struct _krb5_authdata_context_module *dst_module = NULL;
+
+ for (i = 0; i < dst->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &dst->modules[i];
+
+ if (module->ftable == src_module->ftable) {
+ /* XXX is this safe to assume these pointers are interned? */
+ dst_module = module;
+ break;
+ }
+ }
+
+ if (dst_module == NULL)
+ return ENOENT;
+
+ /* copy request context for the first instance only */
+ if (!IS_PRIMARY_INSTANCE(dst_module))
+ return 0;
+
+ assert(strcmp(dst_module->name, src_module->name) == 0);
+
+ /* If copy is unimplemented, externalize/internalize */
+ if (src_module->ftable->copy == NULL) {
+ size_t size = 0, remain;
+ krb5_octet *contents, *bp;
+
+ assert(src_module->ftable->size != NULL);
+ assert(src_module->ftable->externalize != NULL);
+ assert(dst_module->ftable->internalize != NULL);
+
+ code = (*src_module->ftable->size)(kcontext,
+ context,
+ src_module->plugin_context,
+ src_module->request_context,
+ &size);
+ if (code != 0)
+ return code;
+
+ contents = malloc(size);
+ if (contents == NULL)
+ return ENOMEM;
+
+ bp = contents;
+ remain = size;
+
+ code = (*src_module->ftable->externalize)(kcontext,
+ context,
+ src_module->plugin_context,
+ *(src_module->request_context_pp),
+ &bp,
+ &remain);
+ if (code != 0) {
+ free(contents);
+ return code;
+ }
+
+ remain = (bp - contents);
+ bp = contents;
+
+ code = (*dst_module->ftable->internalize)(kcontext,
+ context,
+ dst_module->plugin_context,
+ *(dst_module->request_context_pp),
+ &bp,
+ &remain);
+ if (code != 0) {
+ free(contents);
+ return code;
+ }
+
+ free(contents);
+ } else {
+ assert(src_module->request_context_pp == &src_module->request_context);
+ assert(dst_module->request_context_pp == &dst_module->request_context);
+
+ code = (*src_module->ftable->copy)(kcontext,
+ context,
+ src_module->plugin_context,
+ src_module->request_context,
+ dst_module->plugin_context,
+ dst_module->request_context);
+ }
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_authdata_context_copy(krb5_context kcontext,
+ krb5_authdata_context src,
+ krb5_authdata_context *pdst)
+{
+ int i;
+ krb5_error_code code;
+ krb5_authdata_context dst;
+
+ /* XXX we need to init a new context because we can't copy plugins */
+ code = krb5_authdata_context_init(kcontext, &dst);
+ if (code != 0)
+ return code;
+
+ for (i = 0; i < src->n_modules; i++) {
+ struct _krb5_authdata_context_module *module = &src->modules[i];
+
+ code = k5_copy_ad_module_data(kcontext, src, module, dst);
+ if (code != 0)
+ break;
+ }
+
+ if (code != 0) {
+ krb5_authdata_context_free(kcontext, dst);
+ return code;
+ }
+
+ *pdst = dst;
+
+ return 0;
+}
+
+/*
+ * Calculate size of to-be-externalized authdata context.
+ */
+static krb5_error_code
+krb5_authdata_context_size(krb5_context kcontext,
+ krb5_pointer ptr,
+ size_t *sizep)
+{
+ krb5_error_code code;
+ krb5_authdata_context context = (krb5_authdata_context)ptr;
+
+ code = k5_ad_size(kcontext, context, AD_USAGE_MASK, sizep);
+ if (code != 0)
+ return code;
+
+ *sizep += 2 * sizeof(krb5_int32); /* identifier/trailer */
+
+ return 0;
+}
+
+/*
+ * Externalize an authdata context.
+ */
+static krb5_error_code
+krb5_authdata_context_externalize(krb5_context kcontext,
+ krb5_pointer ptr,
+ krb5_octet **buffer,
+ size_t *lenremain)
+{
+ krb5_error_code code;
+ krb5_authdata_context context = (krb5_authdata_context)ptr;
+ krb5_octet *bp;
+ size_t remain;
+
+ bp = *buffer;
+ remain = *lenremain;
+
+ /* Our identifier */
+ code = krb5_ser_pack_int32(KV5M_AUTHDATA_CONTEXT, &bp, &remain);
+ if (code != 0)
+ return code;
+
+ /* The actual context data */
+ code = k5_ad_externalize(kcontext, context, AD_USAGE_MASK,
+ &bp, &remain);
+ if (code != 0)
+ return code;
+
+ /* Our trailer */
+ code = krb5_ser_pack_int32(KV5M_AUTHDATA_CONTEXT, &bp, &remain);
+ if (code != 0)
+ return code;
+
+ *buffer = bp;
+ *lenremain = remain;
+
+ return 0;
+}
+
+/*
+ * Internalize an authdata context.
+ */
+static krb5_error_code
+krb5_authdata_context_internalize(krb5_context kcontext,
+ krb5_pointer *ptr,
+ krb5_octet **buffer,
+ size_t *lenremain)
+{
+ krb5_error_code code;
+ krb5_authdata_context context;
+ krb5_int32 ibuf;
+ krb5_octet *bp;
+ size_t remain;
+
+ bp = *buffer;
+ remain = *lenremain;
+
+ code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ if (code != 0)
+ return code;
+
+ if (ibuf != KV5M_AUTHDATA_CONTEXT)
+ return EINVAL;
+
+ code = krb5_authdata_context_init(kcontext, &context);
+ if (code != 0)
+ return code;
+
+ code = k5_ad_internalize(kcontext, context, AD_USAGE_MASK,
+ &bp, &remain);
+ if (code != 0) {
+ krb5_authdata_context_free(kcontext, context);
+ return code;
+ }
+
+ code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ if (code != 0)
+ return code;
+
+ if (ibuf != KV5M_AUTHDATA_CONTEXT) {
+ krb5_authdata_context_free(kcontext, context);
+ return EINVAL;
+ }
+
+ *buffer = bp;
+ *lenremain = remain;
+ *ptr = context;
+
+ return 0;
+}
+
+static const krb5_ser_entry krb5_authdata_context_ser_entry = {
+ KV5M_AUTHDATA_CONTEXT,
+ krb5_authdata_context_size,
+ krb5_authdata_context_externalize,
+ krb5_authdata_context_internalize
+};
+
+/*
+ * Register the authdata context serializer.
+ */
+krb5_error_code
+krb5_ser_authdata_context_init(krb5_context kcontext)
+{
+ return krb5_register_serializer(kcontext,
+ &krb5_authdata_context_ser_entry);
+}
+
diff --git a/src/lib/krb5/krb/authdata.h b/src/lib/krb5/krb/authdata.h
new file mode 100644
index 0000000000..9e4dcceb07
--- /dev/null
+++ b/src/lib/krb5/krb/authdata.h
@@ -0,0 +1,48 @@
+/*
+ * lib/krb5/krb/authdata.h
+ *
+ * Copyright (C) 2009 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.
+ *
+ *
+ * <<< Description >>>
+ */
+#ifndef KRB_AUTHDATA_H
+
+#define KRB_AUTHDATA_H
+
+#include <k5-int.h>
+
+/* authdata.c */
+krb5_error_code
+krb5int_authdata_verify(krb5_context context,
+ krb5_authdata_context,
+ krb5_flags usage,
+ const krb5_auth_context *auth_context,
+ const krb5_keyblock *key,
+ const krb5_ap_req *ap_req);
+
+/* pac.c */
+extern krb5plugin_authdata_client_ftable_v0 krb5int_mspac_authdata_client_ftable;
+
+#endif /* !KRB_AUTHDATA_H */
+
diff --git a/src/lib/krb5/krb/copy_auth.c b/src/lib/krb5/krb/copy_auth.c
index dc989acf4d..ba51f38084 100644
--- a/src/lib/krb5/krb/copy_auth.c
+++ b/src/lib/krb5/krb/copy_auth.c
@@ -276,3 +276,126 @@ krb5_error_code krb5int_find_authdata
else krb5_free_authdata(context, fctx.out);
return retval;
}
+
+krb5_error_code KRB5_CALLCONV
+krb5_make_authdata_kdc_issued(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_const_principal issuer,
+ krb5_authdata *const *authdata,
+ krb5_authdata ***ad_kdcissued)
+{
+ krb5_error_code code;
+ krb5_ad_kdcissued ad_kdci;
+ krb5_data *data;
+ krb5_cksumtype cksumtype;
+ krb5_authdata ad_datum;
+ krb5_authdata *ad_data[2];
+
+ *ad_kdcissued = NULL;
+
+ ad_kdci.ad_checksum.contents = NULL;
+ ad_kdci.i_principal = (krb5_principal)issuer;
+ ad_kdci.elements = (krb5_authdata **)authdata;
+
+ code = krb5int_c_mandatory_cksumtype(context, key->enctype,
+ &cksumtype);
+ if (code != 0)
+ return code;
+
+ code = encode_krb5_authdata(ad_kdci.elements, &data);
+ if (code != 0)
+ return code;
+
+ code = krb5_c_make_checksum(context, cksumtype,
+ key, KRB5_KEYUSAGE_AD_KDCISSUED_CKSUM,
+ data, &ad_kdci.ad_checksum);
+ if (code != 0) {
+ krb5_free_data(context, data);
+ return code;
+ }
+
+ krb5_free_data(context, data);
+
+ code = encode_krb5_ad_kdcissued(&ad_kdci, &data);
+ if (code != 0)
+ return code;
+
+ ad_datum.ad_type = KRB5_AUTHDATA_KDC_ISSUED;
+ ad_datum.length = data->length;
+ ad_datum.contents = (unsigned char *)data->data;
+
+ ad_data[0] = &ad_datum;
+ ad_data[1] = NULL;
+
+ code = krb5_copy_authdata(context, ad_data, ad_kdcissued);
+
+ krb5_free_data(context, data);
+ krb5_free_checksum_contents(context, &ad_kdci.ad_checksum);
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_verify_authdata_kdc_issued(krb5_context context,
+ const krb5_keyblock *key,
+ const krb5_authdata *ad_kdcissued,
+ krb5_principal *issuer,
+ krb5_authdata ***authdata)
+{
+ krb5_error_code code;
+ krb5_ad_kdcissued *ad_kdci;
+ krb5_data data, *data2;
+ krb5_boolean valid = FALSE;
+
+ if ((ad_kdcissued->ad_type & AD_TYPE_FIELD_TYPE_MASK) !=
+ KRB5_AUTHDATA_KDC_ISSUED)
+ return EINVAL;
+
+ if (issuer != NULL)
+ *issuer = NULL;
+ if (authdata != NULL)
+ *authdata = NULL;
+
+ data.length = ad_kdcissued->length;
+ data.data = (char *)ad_kdcissued->contents;
+
+ code = decode_krb5_ad_kdcissued(&data, &ad_kdci);
+ if (code != 0)
+ return code;
+
+ code = encode_krb5_authdata(ad_kdci->elements, &data2);
+ if (code != 0) {
+ krb5_free_ad_kdcissued(context, ad_kdci);
+ return code;
+ }
+
+ code = krb5_c_verify_checksum(context, key,
+ KRB5_KEYUSAGE_AD_KDCISSUED_CKSUM,
+ data2, &ad_kdci->ad_checksum, &valid);
+ if (code != 0) {
+ krb5_free_ad_kdcissued(context, ad_kdci);
+ krb5_free_data(context, data2);
+ }
+
+ krb5_free_data(context, data2);
+
+ if (valid == FALSE) {
+ krb5_free_ad_kdcissued(context, ad_kdci);
+ return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ }
+
+ if (issuer != NULL) {
+ *issuer = ad_kdci->i_principal;
+ ad_kdci->i_principal = NULL;
+ }
+
+ if (authdata != NULL) {
+ *authdata = ad_kdci->elements;
+ ad_kdci->elements = NULL;
+ }
+
+ krb5_free_ad_kdcissued(context, ad_kdci);
+
+ return 0;
+}
+
diff --git a/src/lib/krb5/krb/gc_frm_kdc.c b/src/lib/krb5/krb/gc_frm_kdc.c
index b3144c84e2..4102dd728d 100644
--- a/src/lib/krb5/krb/gc_frm_kdc.c
+++ b/src/lib/krb5/krb/gc_frm_kdc.c
@@ -934,6 +934,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
krb5_boolean old_use_conf_ktypes;
char **hrealms;
unsigned int referral_count, i;
+ krb5_authdata **supplied_authdata, **out_supplied_authdata = NULL;
/*
* Set up client and server pointers. Make a fresh and modifyable
@@ -948,8 +949,18 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
krb5_free_principal(context, server);
return retval;
}
+ if (in_cred->authdata != NULL) {
+ if ((retval = krb5_copy_authdata(context, in_cred->authdata,
+ &out_supplied_authdata)) != 0) {
+ krb5_free_principal(context, out_supplied_server);
+ krb5_free_principal(context, server);
+ return retval;
+ }
+ }
+
supplied_server = in_cred->server;
in_cred->server=server;
+ supplied_authdata = in_cred->authdata;
DUMP_PRINC("gc_from_kdc initial client", client);
DUMP_PRINC("gc_from_kdc initial server", server);
@@ -1139,6 +1150,15 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
if (tgtptr == &cc_tgt)
krb5_free_cred_contents(context, tgtptr);
tgtptr=*out_cred;
+ /* Save requested auth data with TGT in case it ends up stored */
+ if (supplied_authdata != NULL) {
+ /* Ensure we note TGT contains authorization data */
+ retval = krb5_copy_authdata(context,
+ supplied_authdata,
+ &(*out_cred)->authdata);
+ if (retval)
+ goto cleanup;
+ }
/* Save pointer to tgt in referral_tgts. */
referral_tgts[referral_count]=*out_cred;
*out_cred = NULL;
@@ -1149,6 +1169,8 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
&server->realm);
if (retval)
goto cleanup;
+ /* Don't ask for KDC to add auth data multiple times */
+ in_cred->authdata = NULL;
/*
* Future work: rewrite server principal per any
* supplied padata.
@@ -1252,7 +1274,6 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
retval = KRB5_PROG_ETYPE_NOSUPP;
goto cleanup;
}
-
context->use_conf_ktypes = old_use_conf_ktypes;
retval = krb5_get_cred_via_tkt(context, tgtptr,
FLAGS2OPTS(tgtptr->ticket_flags) |
@@ -1272,10 +1293,13 @@ cleanup:
server);
krb5_free_principal(context, server);
in_cred->server = supplied_server;
+ in_cred->authdata = supplied_authdata;
if (*out_cred && !retval) {
/* Success: free server, swap supplied server back in. */
krb5_free_principal (context, (*out_cred)->server);
- (*out_cred)->server= out_supplied_server;
+ (*out_cred)->server = out_supplied_server;
+ assert((*out_cred)->authdata == NULL);
+ (*out_cred)->authdata = out_supplied_authdata;
}
else {
/*
@@ -1283,7 +1307,8 @@ cleanup:
* since it's either null or a referral TGT that we free below,
* and we may need it to return.
*/
- krb5_free_principal (context, out_supplied_server);
+ krb5_free_principal(context, out_supplied_server);
+ krb5_free_authdata(context, out_supplied_authdata);
}
DUMP_PRINC("gc_from_kdc: final server after reversion", in_cred->server);
/*
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index 6da6da151d..724e18bf8b 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -47,6 +47,7 @@ krb5_error_code krb5_ser_authenticator_init (krb5_context);
krb5_error_code krb5_ser_checksum_init (krb5_context);
krb5_error_code krb5_ser_keyblock_init (krb5_context);
krb5_error_code krb5_ser_principal_init (krb5_context);
+krb5_error_code krb5_ser_authdata_context_init (krb5_context);
krb5_error_code
krb5_preauth_supply_preauth_data(krb5_context context,
diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c
index 9f5c702e86..801eed0da7 100644
--- a/src/lib/krb5/krb/kfree.c
+++ b/src/lib/krb5/krb/kfree.c
@@ -534,7 +534,8 @@ krb5_free_tkt_authent(krb5_context context, krb5_tkt_authent *val)
void KRB5_CALLCONV
krb5_free_unparsed_name(krb5_context context, char *val)
{
- free(val);
+ if (val != NULL)
+ free(val);
}
void KRB5_CALLCONV
@@ -881,3 +882,30 @@ void krb5_free_fast_armored_req(krb5_context context,
krb5_free_checksum_contents(context, &val->req_checksum);
free(val);
}
+
+void KRB5_CALLCONV
+krb5int_free_data_list(krb5_context context, krb5_data *data)
+{
+ int i;
+
+ if (data == NULL)
+ return;
+
+ for (i = 0; data[i].data != NULL; i++)
+ free(data[i].data);
+
+ free(data);
+}
+
+void KRB5_CALLCONV
+krb5_free_ad_kdcissued(krb5_context context, krb5_ad_kdcissued *val)
+{
+ if (val == NULL)
+ return;
+
+ krb5_free_checksum_contents(context, &val->ad_checksum);
+ krb5_free_principal(context, val->i_principal);
+ krb5_free_authdata(context, val->elements);
+ free(val);
+}
+
diff --git a/src/lib/krb5/krb/mk_req_ext.c b/src/lib/krb5/krb/mk_req_ext.c
index 64eafe3628..df5ac28724 100644
--- a/src/lib/krb5/krb/mk_req_ext.c
+++ b/src/lib/krb5/krb/mk_req_ext.c
@@ -75,6 +75,7 @@ krb5_generate_authenticator (krb5_context,
krb5_authenticator *, krb5_principal,
krb5_checksum *, krb5_keyblock *,
krb5_ui_4, krb5_authdata **,
+ krb5_authdata_context ad_context,
krb5_enctype *desired_etypes,
krb5_enctype tkt_enctype);
@@ -244,6 +245,7 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
(*auth_context)->send_subkey,
(*auth_context)->local_seq_number,
in_creds->authdata,
+ (*auth_context)->ad_context,
desired_etypes,
in_creds->keyblock.enctype)))
goto cleanup_cksum;
@@ -253,12 +255,6 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
&scratch)))
goto cleanup_cksum;
- /* Null out these fields, to prevent pointer sharing problems;
- * they were supplied by the caller
- */
- (*auth_context)->authentp->client = NULL;
- (*auth_context)->authentp->checksum = NULL;
-
/* call the encryption routine */
if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock,
KRB5_KEYUSAGE_AP_REQ_AUTH,
@@ -272,6 +268,13 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
free(toutbuf);
cleanup_cksum:
+ /* Null out these fields, to prevent pointer sharing problems;
+ * they were supplied by the caller
+ */
+ if ((*auth_context)->authentp != NULL) {
+ (*auth_context)->authentp->client = NULL;
+ (*auth_context)->authentp->checksum = NULL;
+ }
if (checksump && checksump->checksum_type != 0x8003)
free(checksump->contents);
@@ -299,11 +302,13 @@ krb5_generate_authenticator(krb5_context context, krb5_authenticator *authent,
krb5_principal client, krb5_checksum *cksum,
krb5_keyblock *key, krb5_ui_4 seq_number,
krb5_authdata **authorization,
+ krb5_authdata_context ad_context,
krb5_enctype *desired_etypes,
krb5_enctype tkt_enctype)
{
krb5_error_code retval;
-
+ krb5_authdata **ext_authdata = NULL;
+
authent->client = client;
authent->checksum = cksum;
if (key) {
@@ -315,12 +320,27 @@ krb5_generate_authenticator(krb5_context context, krb5_authenticator *authent,
authent->seq_number = seq_number;
authent->authorization_data = NULL;
- if (authorization != NULL) {
- retval = krb5_copy_authdata(context, authorization,
- &authent->authorization_data);
+ if (ad_context != NULL) {
+ retval = krb5_authdata_export_authdata(context,
+ ad_context,
+ AD_USAGE_AP_REQ,
+ &ext_authdata);
if (retval)
return retval;
}
+
+ if (authorization != NULL || ext_authdata != NULL) {
+ retval = krb5_merge_authdata(context,
+ authorization,
+ ext_authdata,
+ &authent->authorization_data);
+ if (retval) {
+ krb5_free_authdata(context, ext_authdata);
+ return retval;
+ }
+ krb5_free_authdata(context, ext_authdata);
+ }
+
/* Only send EtypeList if we prefer another enctype to tkt_enctype */
if (desired_etypes != NULL && desired_etypes[0] != tkt_enctype) {
retval = make_etype_list(context, desired_etypes, tkt_enctype,
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index c5a7065624..297e895501 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -8,7 +8,7 @@
* 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
@@ -27,6 +27,7 @@
#include "k5-int.h"
#include "k5-utf8.h"
+#include "authdata.h"
/* draft-brezak-win2k-krb-authz-00 */
@@ -46,9 +47,12 @@ typedef struct _PAC_INFO_BUFFER {
/* ulType */
#define PAC_LOGON_INFO 1
+#define PAC_CREDENTIALS_INFO 2
#define PAC_SERVER_CHECKSUM 6
#define PAC_PRIVSVR_CHECKSUM 7
#define PAC_CLIENT_INFO 10
+#define PAC_DELEGATION_INFO 11
+#define PAC_UPN_DNS_INFO 12
typedef struct _PACTYPE {
krb5_ui_4 cBuffers;
@@ -66,6 +70,7 @@ typedef struct _PACTYPE {
struct krb5_pac_data {
PACTYPE *pac; /* PAC header + info buffer array */
krb5_data data; /* PAC data (including uninitialised header) */
+ krb5_boolean verified;
};
static krb5_error_code
@@ -93,7 +98,7 @@ k5_pac_add_buffer(krb5_context context,
/* Check there isn't already a buffer of this type */
if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) {
- return EINVAL;
+ return EEXIST;
}
header = (PACTYPE *)realloc(pac->pac,
@@ -148,6 +153,8 @@ k5_pac_add_buffer(krb5_context context,
out_data->length = data->length;
}
+ pac->verified = FALSE;
+
return 0;
}
@@ -228,7 +235,7 @@ krb5_pac_get_buffer(krb5_context context,
ret = k5_pac_locate_buffer(context, pac, type, &d);
if (ret != 0)
return ret;
-
+
data->data = malloc(d.length);
if (data->data == NULL)
return ENOMEM;
@@ -277,7 +284,7 @@ krb5_pac_init(krb5_context context,
pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE));
if (pac->pac == NULL) {
- free( pac);
+ free(pac);
return ENOMEM;
}
@@ -291,11 +298,54 @@ krb5_pac_init(krb5_context context,
return ENOMEM;
}
+ pac->verified = FALSE;
+
*ppac = pac;
return 0;
}
+static krb5_error_code
+k5_pac_copy(krb5_context context,
+ krb5_pac src,
+ krb5_pac *dst)
+{
+ size_t header_len;
+ krb5_ui_4 cbuffers;
+ krb5_error_code code;
+ krb5_pac pac;
+
+ cbuffers = src->pac->cBuffers;
+ if (cbuffers != 0)
+ cbuffers--;
+
+ header_len = sizeof(PACTYPE) + cbuffers * sizeof(PAC_INFO_BUFFER);
+
+ pac = (krb5_pac)malloc(sizeof(*pac));
+ if (pac == NULL)
+ return ENOMEM;
+
+ pac->pac = (PACTYPE *)malloc(header_len);
+ if (pac->pac == NULL) {
+ free(pac);
+ return ENOMEM;
+ }
+
+ memcpy(pac->pac, src->pac, header_len);
+
+ code = krb5int_copy_data_contents(context, &src->data, &pac->data);
+ if (code != 0) {
+ free(pac->pac);
+ free(pac);
+ return ENOMEM;
+ }
+
+ pac->verified = src->verified;
+ *dst = pac;
+
+ return 0;
+}
+
/*
* Parse the supplied data into the PAC allocated by this function
*/
@@ -379,7 +429,8 @@ krb5_pac_parse(krb5_context context,
}
static krb5_error_code
-k5_time_to_seconds_since_1970(krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
+k5_time_to_seconds_since_1970(krb5_int64 ntTime,
+ krb5_timestamp *elapsedSeconds)
{
krb5_ui_8 abstime;
@@ -393,10 +444,11 @@ k5_time_to_seconds_since_1970(krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
*elapsedSeconds = abstime;
return 0;
-}
+}
static krb5_error_code
-k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime)
+k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds,
+ krb5_ui_8 *ntTime)
{
*ntTime = elapsedSeconds;
@@ -404,7 +456,7 @@ k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime)
*ntTime += NT_TIME_EPOCH;
*ntTime *= 10000000;
-
+
return 0;
}
@@ -441,10 +493,11 @@ k5_pac_validate_client(krb5_context context,
return ret;
if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
- pac_princname_length % 2)
+ pac_princname_length % 2)
return ERANGE;
- ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL);
+ ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2,
+ &pac_princname, NULL);
if (ret != 0)
return ret;
@@ -457,7 +510,10 @@ k5_pac_validate_client(krb5_context context,
free(pac_princname);
if (pac_authtime != authtime ||
- krb5_principal_compare(context, pac_principal, principal) == FALSE)
+ !krb5_principal_compare_flags(context,
+ pac_principal,
+ principal,
+ KRB5_PRINCIPAL_COMPARE_IGNORE_REALM))
ret = KRB5KRB_AP_WRONG_PRINC;
krb5_free_principal(context, pac_principal);
@@ -513,7 +569,8 @@ k5_pac_verify_server_checksum(krb5_context context,
krb5_boolean valid;
krb5_octet *p;
- ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &checksum_data);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_SERVER_CHECKSUM, &checksum_data);
if (ret != 0)
return ret;
@@ -533,19 +590,22 @@ k5_pac_verify_server_checksum(krb5_context context,
memcpy(pac_data.data, pac->data.data, pac->data.length);
/* Zero out both checksum buffers */
- ret = k5_pac_zero_signature(context, pac, PAC_SERVER_CHECKSUM, &pac_data);
+ ret = k5_pac_zero_signature(context, pac,
+ PAC_SERVER_CHECKSUM, &pac_data);
if (ret != 0) {
free(pac_data.data);
return ret;
}
- ret = k5_pac_zero_signature(context, pac, PAC_PRIVSVR_CHECKSUM, &pac_data);
+ ret = k5_pac_zero_signature(context, pac,
+ PAC_PRIVSVR_CHECKSUM, &pac_data);
if (ret != 0) {
free(pac_data.data);
return ret;
}
- ret = krb5_c_verify_checksum(context, server, KRB5_KEYUSAGE_APP_DATA_CKSUM,
+ ret = krb5_c_verify_checksum(context, server,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM,
&pac_data, &checksum, &valid);
free(pac_data.data);
@@ -571,14 +631,16 @@ k5_pac_verify_kdc_checksum(krb5_context context,
krb5_boolean valid;
krb5_octet *p;
- ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
if (ret != 0)
return ret;
if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
return KRB5_BAD_MSIZE;
- ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_checksum);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_SERVER_CHECKSUM, &server_checksum);
if (ret != 0)
return ret;
@@ -593,7 +655,8 @@ k5_pac_verify_kdc_checksum(krb5_context context,
server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
- ret = krb5_c_verify_checksum(context, privsvr, KRB5_KEYUSAGE_APP_DATA_CKSUM,
+ ret = krb5_c_verify_checksum(context, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM,
&server_checksum, &checksum, &valid);
if (ret != 0)
return ret;
@@ -633,6 +696,8 @@ krb5_pac_verify(krb5_context context,
return ret;
}
+ pac->verified = TRUE;
+
return 0;
}
@@ -650,12 +715,14 @@ k5_insert_client_info(krb5_context context,
krb5_ui_8 nt_authtime;
/* If we already have a CLIENT_INFO buffer, then just validate it */
- if (k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info) == 0) {
+ if (k5_pac_locate_buffer(context, pac,
+ PAC_CLIENT_INFO, &client_info) == 0) {
return k5_pac_validate_client(context, pac, authtime, principal);
}
ret = krb5_unparse_name_flags(context, principal,
- KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_name_utf8);
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &princ_name_utf8);
if (ret != 0)
goto cleanup;
@@ -668,7 +735,8 @@ k5_insert_client_info(krb5_context context,
client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len;
client_info.data = NULL;
- ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO, &client_info, TRUE, &client_info);
+ ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO,
+ &client_info, TRUE, &client_info);
if (ret != 0)
goto cleanup;
@@ -685,12 +753,11 @@ k5_insert_client_info(krb5_context context,
/* copy in principal name */
memcpy(p, princ_name_ucs2, princ_name_ucs2_len);
-
+
cleanup:
- if (princ_name_utf8 != NULL)
- free(princ_name_utf8);
if (princ_name_ucs2 != NULL)
free(princ_name_ucs2);
+ krb5_free_unparsed_name(context, princ_name_utf8);
return ret;
}
@@ -716,7 +783,10 @@ k5_insert_checksum(krb5_context context,
ret = k5_pac_locate_buffer(context, pac, type, &cksumdata);
if (ret == 0) {
- /* If we're resigning PAC, make sure we can fit checksum into existing buffer */
+ /*
+ * If we're resigning PAC, make sure we can fit checksum
+ * into existing buffer
+ */
if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len)
return ERANGE;
@@ -726,7 +796,9 @@ k5_insert_checksum(krb5_context context,
cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len;
cksumdata.data = NULL;
- ret = k5_pac_add_buffer(context, pac, type, &cksumdata, TRUE, &cksumdata);
+ ret = k5_pac_add_buffer(context, pac,
+ type, &cksumdata,
+ TRUE, &cksumdata);
if (ret != 0)
return ret;
}
@@ -745,7 +817,8 @@ k5_pac_encode_header(krb5_context context, krb5_pac pac)
unsigned char *p;
size_t header_len;
- header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
+ header_len = PACTYPE_LENGTH +
+ (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
assert(pac->data.length >= header_len);
p = (unsigned char *)pac->data.data;
@@ -818,7 +891,8 @@ krb5int_pac_sign(krb5_context context,
return ret;
/* Generate the server checksum over the entire PAC */
- ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_cksum);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_SERVER_CHECKSUM, &server_cksum);
if (ret != 0)
return ret;
@@ -838,7 +912,8 @@ krb5int_pac_sign(krb5_context context,
return ret;
/* Generate the privsvr checksum over the server checksum buffer */
- ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_cksum);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_PRIVSVR_CHECKSUM, &privsvr_cksum);
if (ret != 0)
return ret;
@@ -865,8 +940,603 @@ krb5int_pac_sign(krb5_context context,
data->length = pac->data.length;
memcpy(data->data, pac->data.data, pac->data.length);
- memset(pac->data.data, 0, PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));
+ memset(pac->data.data, 0,
+ PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));
+
+ return 0;
+}
+
+/*
+ * PAC auth data attribute backend
+ */
+struct mspac_context {
+ krb5_pac pac;
+};
+
+static krb5_error_code
+mspac_init(krb5_context kcontext, void **plugin_context)
+{
+ *plugin_context = NULL;
+ return 0;
+}
+
+static void
+mspac_flags(krb5_context kcontext,
+ void *plugin_context,
+ krb5_authdatatype ad_type,
+ krb5_flags *flags)
+{
+ *flags = AD_USAGE_KDC_ISSUED;
+}
+
+static void
+mspac_fini(krb5_context kcontext, void *plugin_context)
+{
+ return;
+}
+
+static krb5_error_code
+mspac_request_init(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void **request_context)
+{
+ struct mspac_context *pacctx;
+
+ pacctx = (struct mspac_context *)malloc(sizeof(*pacctx));
+ if (pacctx == NULL)
+ return ENOMEM;
+
+ pacctx->pac = NULL;
+
+ *request_context = pacctx;
+
+ return 0;
+}
+
+static krb5_error_code
+mspac_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)
+{
+ krb5_error_code code;
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+
+ if (kdc_issued)
+ return EINVAL;
+
+ if (pacctx->pac != NULL) {
+ krb5_pac_free(kcontext, pacctx->pac);
+ pacctx->pac = NULL;
+ }
+
+ assert(authdata[0] != NULL);
+ assert((authdata[0]->ad_type & AD_TYPE_FIELD_TYPE_MASK) ==
+ KRB5_AUTHDATA_WIN2K_PAC);
+
+ code = krb5_pac_parse(kcontext, authdata[0]->contents,
+ authdata[0]->length, &pacctx->pac);
+
+ return code;
+}
+
+static krb5_error_code
+mspac_export_authdata(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_flags usage,
+ krb5_authdata ***out_authdata)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_authdata **authdata;
+ krb5_data data;
+
+ if (pacctx->pac == NULL)
+ return 0;
+
+ authdata = calloc(2, sizeof(krb5_authdata *));
+ if (authdata == NULL)
+ return ENOMEM;
+
+ authdata[0] = calloc(1, sizeof(krb5_authdata));
+ if (authdata[0] == NULL) {
+ free(authdata);
+ return ENOMEM;
+ }
+ authdata[1] = NULL;
+
+ code = krb5int_copy_data_contents(kcontext, &pacctx->pac->data, &data);
+ if (code != 0) {
+ krb5_free_authdata(kcontext, authdata);
+ return code;
+ }
+
+ authdata[0]->magic = KV5M_AUTHDATA;
+ authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC;
+ authdata[0]->length = data.length;
+ authdata[0]->contents = (krb5_octet *)data.data;
+
+ authdata[1] = NULL;
+
+ *out_authdata = authdata;
+
+ return 0;
+}
+
+static krb5_error_code
+mspac_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)
+{
+ krb5_error_code code;
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+
+ if (pacctx->pac == NULL)
+ return EINVAL;
+
+ code = krb5_pac_verify(kcontext,
+ pacctx->pac,
+ req->ticket->enc_part2->times.authtime,
+ req->ticket->enc_part2->client,
+ key,
+ NULL);
+
+#if 0
+ /*
+ * Now, we could return 0 and just set pac->verified to FALSE.
+ * Thoughts?
+ */
+ if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ assert(pacctx->pac->verified == FALSE);
+ code = 0;
+ }
+#endif
+
+ return code;
+}
+
+static void
+mspac_request_fini(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+
+ if (pacctx != NULL) {
+ if (pacctx->pac != NULL)
+ krb5_pac_free(kcontext, pacctx->pac);
+
+ free(pacctx);
+ }
+}
+
+#define STRLENOF(x) (sizeof((x)) - 1)
+
+static struct {
+ krb5_ui_4 type;
+ krb5_data attribute;
+} mspac_attribute_types[] = {
+ { (krb5_ui_4)-1, { KV5M_DATA, STRLENOF("mspac:"), "mspac:" } },
+ { PAC_LOGON_INFO, { KV5M_DATA, STRLENOF("mspac:logon-info"), "mspac:logon-info" } },
+ { PAC_CREDENTIALS_INFO, { KV5M_DATA, STRLENOF("mspac:credentials-info"), "mspac:credentials-info" } },
+ { PAC_SERVER_CHECKSUM, { KV5M_DATA, STRLENOF("mspac:server-checksum"), "mspac:server-checksum" } },
+ { PAC_PRIVSVR_CHECKSUM, { KV5M_DATA, STRLENOF("mspac:privsvr-checksum"), "mspac:privsvr-checksum" } },
+ { PAC_CLIENT_INFO, { KV5M_DATA, STRLENOF("mspac:client-info"), "mspac:client-info" } },
+ { PAC_DELEGATION_INFO, { KV5M_DATA, STRLENOF("mspac:delegation-info"), "mspac:delegation-info" } },
+ { PAC_UPN_DNS_INFO, { KV5M_DATA, STRLENOF("mspac:upn-dns-info"), "mspac:upn-dns-info" } },
+};
+
+#define MSPAC_ATTRIBUTE_COUNT (sizeof(mspac_attribute_types)/sizeof(mspac_attribute_types[0]))
+
+static krb5_error_code
+mspac_type2attr(krb5_ui_4 type, krb5_data *attr)
+{
+ unsigned int i;
+
+ for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
+ if (mspac_attribute_types[i].type == type) {
+ *attr = mspac_attribute_types[i].attribute;
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+static krb5_error_code
+mspac_attr2type(const krb5_data *attr, krb5_ui_4 *type)
+{
+ unsigned int i;
+
+ for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
+ if (attr->length == mspac_attribute_types[i].attribute.length &&
+ strncasecmp(attr->data, mspac_attribute_types[i].attribute.data, attr->length) == 0) {
+ *type = mspac_attribute_types[i].type;
+ return 0;
+ }
+ }
+
+ if (attr->length > STRLENOF("mspac:") &&
+ strncasecmp(attr->data, "mspac:", STRLENOF("mspac:")) == 0)
+ {
+ char *p = &attr->data[STRLENOF("mspac:")];
+ char *endptr;
+
+ *type = strtoul(p, &endptr, 10);
+ if (*type != 0 && *endptr == '\0')
+ return 0;
+ }
+
+ return ENOENT;
+}
+
+static krb5_error_code
+mspac_get_attribute_types(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_data **out_attrs)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ unsigned int i, j;
+ krb5_data *attrs;
+ krb5_error_code code;
+
+ if (pacctx->pac == NULL)
+ return ENOENT;
+
+ attrs = calloc(1 + pacctx->pac->pac->cBuffers + 1, sizeof(krb5_data));
+ if (attrs == NULL)
+ return ENOMEM;
+
+ j = 0;
+
+ /* The entire PAC */
+ code = krb5int_copy_data_contents(kcontext,
+ &mspac_attribute_types[0].attribute,
+ &attrs[j++]);
+ if (code != 0) {
+ free(attrs);
+ return code;
+ }
+
+ /* PAC buffers */
+ for (i = 0; i < pacctx->pac->pac->cBuffers; i++) {
+ krb5_data attr;
+
+ code = mspac_type2attr(pacctx->pac->pac->Buffers[i].ulType, &attr);
+ if (code == 0) {
+ code = krb5int_copy_data_contents(kcontext, &attr, &attrs[j++]);
+ if (code != 0) {
+ krb5int_free_data_list(kcontext, attrs);
+ return code;
+ }
+ } else {
+ int length;
+
+ length = asprintf(&attrs[j].data, "mspac:%d",
+ pacctx->pac->pac->Buffers[i].ulType);
+ if (length < 0) {
+ krb5int_free_data_list(kcontext, attrs);
+ return ENOMEM;
+ }
+ attrs[j++].length = length;
+ }
+ }
+ attrs[j].data = NULL;
+ attrs[j].length = 0;
+
+ *out_attrs = attrs;
return 0;
}
+static krb5_error_code
+mspac_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 mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_ui_4 type;
+
+ value->data = NULL;
+ value->length = 0;
+
+ if (display_value != NULL) {
+ display_value->data = NULL;
+ display_value->length = 0;
+ }
+
+ if (*more != -1 || pacctx->pac == NULL)
+ return ENOENT;
+
+ code = mspac_attr2type(attribute, &type);
+ if (code != 0)
+ return code;
+
+ /* -1 is a magic type that refers to the entire PAC */
+ if (type == (krb5_ui_4)-1) {
+ if (value != NULL)
+ code = krb5int_copy_data_contents(kcontext,
+ &pacctx->pac->data,
+ value);
+ else
+ code = 0;
+ } else {
+ if (value != NULL)
+ code = krb5_pac_get_buffer(kcontext, pacctx->pac, type, value);
+ else
+ code = k5_pac_locate_buffer(kcontext, pacctx->pac, type, NULL);
+ }
+ if (code == 0) {
+ *authenticated = pacctx->pac->verified;
+ *complete = TRUE;
+ }
+
+ *more = 0;
+
+ return code;
+}
+
+static krb5_error_code
+mspac_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)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_ui_4 type;
+
+ if (pacctx->pac == NULL)
+ return ENOENT;
+
+ code = mspac_attr2type(attribute, &type);
+ if (code != 0)
+ return code;
+
+ /* -1 is a magic type that refers to the entire PAC */
+ if (type == (krb5_ui_4)-1) {
+ krb5_pac newpac;
+
+ code = krb5_pac_parse(kcontext, value->data, value->length, &newpac);
+ if (code != 0)
+ return code;
+
+ krb5_pac_free(kcontext, pacctx->pac);
+ pacctx->pac = newpac;
+ } else {
+ code = krb5_pac_add_buffer(kcontext, pacctx->pac, type, value);
+ }
+
+ return code;
+}
+
+static krb5_error_code
+mspac_export_internal(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_boolean restrict_authenticated,
+ void **ptr)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_pac pac;
+
+ *ptr = NULL;
+
+ if (pacctx->pac == NULL)
+ return 0;
+
+ if (restrict_authenticated && (pacctx->pac->verified) == FALSE)
+ return 0;
+
+ code = krb5_pac_parse(kcontext, pacctx->pac->data.data,
+ pacctx->pac->data.length, &pac);
+ if (code == 0) {
+ pac->verified = pacctx->pac->verified;
+ *ptr = pac;
+ }
+
+ return code;
+}
+
+static void
+mspac_free_internal(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ void *ptr)
+{
+ if (ptr != NULL)
+ krb5_pac_free(kcontext, (krb5_pac)ptr);
+
+ return;
+}
+
+static krb5_error_code
+mspac_size(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ size_t *sizep)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+
+ *sizep += sizeof(krb5_int32);
+
+ if (pacctx->pac != NULL)
+ *sizep += pacctx->pac->data.length;
+
+ *sizep += sizeof(krb5_int32);
+
+ return 0;
+}
+
+static krb5_error_code
+mspac_externalize(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_octet **buffer,
+ size_t *lenremain)
+{
+ krb5_error_code code = 0;
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ size_t required = 0;
+ krb5_octet *bp;
+ size_t remain;
+
+ bp = *buffer;
+ remain = *lenremain;
+
+ if (pacctx->pac != NULL) {
+ mspac_size(kcontext, context, plugin_context,
+ request_context, &required);
+
+ if (required <= remain) {
+ krb5_ser_pack_int32((krb5_int32)pacctx->pac->data.length,
+ &bp, &remain);
+ krb5_ser_pack_bytes((krb5_octet *)pacctx->pac->data.data,
+ (size_t)pacctx->pac->data.length,
+ &bp, &remain);
+ krb5_ser_pack_int32((krb5_int32)pacctx->pac->verified,
+ &bp, &remain);
+ } else {
+ code = ENOMEM;
+ }
+ } else {
+ krb5_ser_pack_int32(0, &bp, &remain); /* length */
+ krb5_ser_pack_int32(0, &bp, &remain); /* verified */
+ }
+
+ *buffer = bp;
+ *lenremain = remain;
+
+ return code;
+}
+
+static krb5_error_code
+mspac_internalize(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_octet **buffer,
+ size_t *lenremain)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_int32 ibuf;
+ krb5_octet *bp;
+ size_t remain;
+ krb5_pac pac = NULL;
+
+ bp = *buffer;
+ remain = *lenremain;
+
+ /* length */
+ code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ if (code != 0)
+ return code;
+
+ if (ibuf != 0) {
+ code = krb5_pac_parse(kcontext, bp, ibuf, &pac);
+ if (code != 0)
+ return code;
+
+ bp += ibuf;
+ remain -= ibuf;
+ }
+
+ /* verified */
+ code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ if (code != 0) {
+ krb5_pac_free(kcontext, pac);
+ return code;
+ }
+
+ if (pac != NULL) {
+ pac->verified = (ibuf != 0);
+ }
+
+ if (pacctx->pac != NULL) {
+ krb5_pac_free(kcontext, pacctx->pac);
+ }
+
+ pacctx->pac = pac;
+
+ *buffer = bp;
+ *lenremain = remain;
+
+ return 0;
+}
+
+static krb5_error_code
+mspac_copy(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ void *dst_plugin_context,
+ void *dst_request_context)
+{
+ struct mspac_context *srcctx = (struct mspac_context *)request_context;
+ struct mspac_context *dstctx = (struct mspac_context *)dst_request_context;
+ krb5_error_code code = 0;
+
+ assert(dstctx != NULL);
+ assert(dstctx->pac == NULL);
+
+ if (srcctx->pac != NULL)
+ code = k5_pac_copy(kcontext, srcctx->pac, &dstctx->pac);
+
+ return code;
+}
+
+static krb5_authdatatype mspac_ad_types[] = { KRB5_AUTHDATA_WIN2K_PAC, 0 };
+
+krb5plugin_authdata_client_ftable_v0 krb5int_mspac_authdata_client_ftable = {
+ "mspac",
+ mspac_ad_types,
+ mspac_init,
+ mspac_fini,
+ mspac_flags,
+ mspac_request_init,
+ mspac_request_fini,
+ mspac_get_attribute_types,
+ mspac_get_attribute,
+ mspac_set_attribute,
+ NULL, /* delete_attribute_proc */
+ mspac_export_authdata,
+ mspac_import_authdata,
+ mspac_export_internal,
+ mspac_free_internal,
+ mspac_verify,
+ mspac_size,
+ mspac_externalize,
+ mspac_internalize,
+ mspac_copy
+};
+
diff --git a/src/lib/krb5/krb/rd_req.c b/src/lib/krb5/krb/rd_req.c
index 5848aa776f..50c3a90111 100644
--- a/src/lib/krb5/krb/rd_req.c
+++ b/src/lib/krb5/krb/rd_req.c
@@ -8,7 +8,7 @@
* 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
@@ -22,7 +22,7 @@
* 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.
- *
+ *
*
* krb5_rd_req()
*/
@@ -32,16 +32,16 @@
/*
* Parses a KRB_AP_REQ message, returning its contents.
- *
+ *
* server specifies the expected server's name for the ticket.
- *
+ *
* keyproc specifies a procedure to generate a decryption key for the
* ticket. If keyproc is non-NULL, keyprocarg is passed to it, and the result
* used as a decryption key. If keyproc is NULL, then fetchfrom is checked;
* if it is non-NULL, it specifies a parameter name from which to retrieve the
* decryption key. If fetchfrom is NULL, then the default key store is
* consulted.
- *
+ *
* returns system errors, encryption errors, replay errors
*/
@@ -58,14 +58,14 @@ krb5_rd_req(krb5_context context, krb5_auth_context *auth_context,
if (!krb5_is_ap_req(inbuf))
return KRB5KRB_AP_ERR_MSG_TYPE;
-#ifndef LEAN_CLIENT
+#ifndef LEAN_CLIENT
if ((retval = decode_krb5_ap_req(inbuf, &request))) {
switch (retval) {
case KRB5_BADMSGTYPE:
- return KRB5KRB_AP_ERR_BADVERSION;
+ return KRB5KRB_AP_ERR_BADVERSION;
default:
return(retval);
- }
+ }
}
#endif /* LEAN_CLIENT */
@@ -78,7 +78,7 @@ krb5_rd_req(krb5_context context, krb5_auth_context *auth_context,
}
-#ifndef LEAN_CLIENT
+#ifndef LEAN_CLIENT
/* Get a keytab if necessary. */
if (keytab == NULL) {
if ((retval = krb5_kt_default(context, &new_keytab)))
@@ -87,10 +87,10 @@ krb5_rd_req(krb5_context context, krb5_auth_context *auth_context,
}
#endif /* LEAN_CLIENT */
- retval = krb5_rd_req_decoded(context, auth_context, request, server,
+ retval = krb5_rd_req_decoded(context, auth_context, request, server,
keytab, ap_req_options, ticket);
-#ifndef LEAN_CLIENT
+#ifndef LEAN_CLIENT
if (new_keytab != NULL)
(void) krb5_kt_close(context, new_keytab);
#endif /* LEAN_CLIENT */
diff --git a/src/lib/krb5/krb/rd_req_dec.c b/src/lib/krb5/krb/rd_req_dec.c
index c618be1eea..6edf6d7600 100644
--- a/src/lib/krb5/krb/rd_req_dec.c
+++ b/src/lib/krb5/krb/rd_req_dec.c
@@ -31,6 +31,8 @@
#include "k5-int.h"
#include "auth_con.h"
+#include "authdata.h"
+#include "int-proto.h"
/*
* essentially the same as krb_rd_req, but uses a decoded AP_REQ as
@@ -92,7 +94,8 @@ krb5int_check_clockskew(krb5_context context, krb5_timestamp date)
static krb5_error_code
krb5_rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req,
- krb5_const_principal server, krb5_keytab keytab)
+ krb5_const_principal server, krb5_keytab keytab,
+ krb5_keyblock *key)
{
krb5_error_code retval;
krb5_keytab_entry ktent;
@@ -107,10 +110,12 @@ krb5_rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req,
req->ticket->enc_part.enctype, &ktent);
if (retval == 0) {
retval = krb5_decrypt_tkt_part(context, &ktent.key, req->ticket);
+ if (retval == 0 && key != NULL)
+ retval = krb5_copy_keyblock_contents(context, &ktent.key, key);
(void) krb5_free_keytab_entry_contents(context, &ktent);
}
- } else {
+ } else {
krb5_error_code code;
krb5_kt_cursor cursor;
@@ -142,6 +147,8 @@ krb5_rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req,
* server as it appeared in the ticket.
*/
retval = krb5_copy_principal(context, ktent.principal, &tmp);
+ if (retval == 0 && key != NULL)
+ retval = krb5_copy_keyblock_contents(context, &ktent.key, key);
if (retval == 0) {
krb5_free_principal(context, req->ticket->server);
req->ticket->server = tmp;
@@ -204,11 +211,15 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
{
krb5_error_code retval = 0;
krb5_principal_data princ_data;
- krb5_enctype *desired_etypes = NULL;
+ krb5_enctype *desired_etypes = NULL;
int desired_etypes_len = 0;
int rfc4537_etypes_len = 0;
- krb5_enctype *permitted_etypes = NULL;
+ krb5_enctype *permitted_etypes = NULL;
int permitted_etypes_len = 0;
+ krb5_keyblock decrypt_key;
+
+ decrypt_key.enctype = ENCTYPE_NULL;
+ decrypt_key.contents = NULL;
req->ticket->enc_part2 = NULL;
if (server && krb5_is_referral_realm(&server->realm)) {
@@ -231,14 +242,20 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
if ((retval = krb5_decrypt_tkt_part(context, (*auth_context)->keyblock,
req->ticket)))
goto cleanup;
- krb5_free_keyblock(context, (*auth_context)->keyblock);
+ if (check_valid_flag) {
+ decrypt_key = *((*auth_context)->keyblock);
+ free((*auth_context)->keyblock);
+ } else
+ krb5_free_keyblock(context, (*auth_context)->keyblock);
(*auth_context)->keyblock = NULL;
} else {
- if ((retval = krb5_rd_req_decrypt_tkt_part(context, req, server, keytab)))
+ if ((retval = krb5_rd_req_decrypt_tkt_part(context, req,
+ server, keytab,
+ check_valid_flag ? &decrypt_key : NULL)))
goto cleanup;
}
- /* XXX this is an evil hack. check_valid_flag is set iff the call
+ /* XXX this is an evil hack. check_valid_flag is set iff the call
is not from inside the kdc. we can use this to determine which
key usage to use */
#ifndef LEAN_CLIENT
@@ -284,7 +301,7 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
/* If the transited list is empty, then we have at most one hop */
if (trans->tr_contents.data && trans->tr_contents.data[0])
- retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
+ retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
}
#elif defined(_NO_CROSS_REALM)
@@ -325,7 +342,7 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
/*
* If the transited list is not empty, then check that all realms
* transited are within the hierarchy between the client's realm
- * and the local realm.
+ * and the local realm.
*/
if (trans->tr_contents.data && trans->tr_contents.data[0]) {
retval = krb5_check_transited_list(context, &(trans->tr_contents),
@@ -344,7 +361,7 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
if ((*auth_context)->rcache) {
krb5_donot_replay rep;
- krb5_tkt_authent tktauthent;
+ krb5_tkt_authent tktauthent;
tktauthent.ticket = req->ticket;
tktauthent.authenticator = (*auth_context)->authentp;
@@ -376,6 +393,17 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
retval = KRB5KRB_AP_ERR_TKT_INVALID;
goto cleanup;
}
+
+ if ((retval = krb5_authdata_context_init(context,
+ &(*auth_context)->ad_context)))
+ goto cleanup;
+ if ((retval = krb5int_authdata_verify(context,
+ (*auth_context)->ad_context,
+ AD_USAGE_MASK,
+ auth_context,
+ &decrypt_key,
+ req)))
+ goto cleanup;
}
/* read RFC 4537 etype list from sender */
@@ -520,18 +548,21 @@ cleanup:
krb5_free_enc_tkt_part(context, req->ticket->enc_part2);
req->ticket->enc_part2 = NULL;
}
+ if (check_valid_flag)
+ krb5_free_keyblock_contents(context, &decrypt_key);
+
return retval;
}
krb5_error_code
krb5_rd_req_decoded(krb5_context context, krb5_auth_context *auth_context,
- const krb5_ap_req *req, krb5_const_principal server,
- krb5_keytab keytab, krb5_flags *ap_req_options,
- krb5_ticket **ticket)
+ const krb5_ap_req *req, krb5_const_principal server,
+ krb5_keytab keytab, krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
{
krb5_error_code retval;
retval = krb5_rd_req_decoded_opt(context, auth_context,
- req, server, keytab,
+ req, server, keytab,
ap_req_options, ticket,
1); /* check_valid_flag */
return retval;
@@ -539,14 +570,14 @@ krb5_rd_req_decoded(krb5_context context, krb5_auth_context *auth_context,
krb5_error_code
krb5_rd_req_decoded_anyflag(krb5_context context,
- krb5_auth_context *auth_context,
- const krb5_ap_req *req,
- krb5_const_principal server, krb5_keytab keytab,
- krb5_flags *ap_req_options, krb5_ticket **ticket)
+ krb5_auth_context *auth_context,
+ const krb5_ap_req *req,
+ krb5_const_principal server, krb5_keytab keytab,
+ krb5_flags *ap_req_options, krb5_ticket **ticket)
{
krb5_error_code retval;
retval = krb5_rd_req_decoded_opt(context, auth_context,
- req, server, keytab,
+ req, server, keytab,
ap_req_options, ticket,
0); /* don't check_valid_flag */
return retval;
diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
index 883d33cc77..a7e5199026 100644
--- a/src/lib/krb5/krb/s4u_creds.c
+++ b/src/lib/krb5/krb/s4u_creds.c
@@ -115,7 +115,7 @@ s4u_identify_user(krb5_context context,
client = &client_data;
}
- code = krb5_get_init_creds(context, &creds, in_creds->client,
+ code = krb5_get_init_creds(context, &creds, client,
NULL, NULL, 0, NULL, opte,
krb5_get_as_key_noop, &userid,
&use_master, NULL);
diff --git a/src/lib/krb5/krb/ser_actx.c b/src/lib/krb5/krb/ser_actx.c
index 347b300f55..487455b9d6 100644
--- a/src/lib/krb5/krb/ser_actx.c
+++ b/src/lib/krb5/krb/ser_actx.c
@@ -560,5 +560,7 @@ krb5_ser_auth_context_init(krb5_context kcontext)
kret = krb5_ser_keyblock_init(kcontext);
if (!kret)
kret = krb5_ser_principal_init(kcontext);
+ if (!kret)
+ kret = krb5_ser_authdata_context_init(kcontext);
return(kret);
}
diff --git a/src/lib/krb5/krb/t_authdata.c b/src/lib/krb5/krb/t_authdata.c
index 8b786875f5..86838cead3 100644
--- a/src/lib/krb5/krb/t_authdata.c
+++ b/src/lib/krb5/krb/t_authdata.c
@@ -65,6 +65,13 @@ krb5_authdata *adseq1[] = {&ad1, &ad2, &ad4, NULL};
krb5_authdata *adseq2[] = {&ad3, NULL};
+krb5_keyblock key = {
+ KV5M_KEYBLOCK,
+ ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+ 16,
+ (unsigned char *)"1234567890ABCDEF"
+};
+
static void compare_authdata(const krb5_authdata *adc1, krb5_authdata *adc2) {
assert(adc1->ad_type == adc2->ad_type);
assert(adc1->length == adc2->length);
@@ -77,7 +84,7 @@ int main()
krb5_authdata **results;
krb5_authdata *container[2];
krb5_authdata **container_out;
-
+ krb5_authdata **kdci;
assert(krb5_init_context(&context) == 0);
assert(krb5_merge_authdata(context, adseq1, adseq2, &results) == 0);
@@ -96,6 +103,13 @@ int main()
compare_authdata( results[1], &ad4);
compare_authdata( results[2], &ad3);
assert( results[3] == NULL);
+ krb5_free_authdata(context, container_out);
+ assert(krb5_make_authdata_kdc_issued(context, &key, NULL, results, &kdci) == 0);
+ assert(krb5_verify_authdata_kdc_issued(context, &key, kdci[0], NULL, &container_out) == 0);
+ compare_authdata(container_out[0], results[0]);
+ compare_authdata(container_out[1], results[1]);
+ compare_authdata(container_out[2], results[2]);
+ krb5_free_authdata(context, kdci);
krb5_free_authdata(context, results);
krb5_free_authdata(context, container_out);
krb5_free_context(context);
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index b809e83cf7..2735c9f985 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -1,4 +1,5 @@
_krb5_conf_boolean
+decode_krb5_ad_kdcissued
decode_krb5_alt_method
decode_krb5_ap_rep
decode_krb5_ap_rep_enc_part
@@ -40,6 +41,7 @@ decode_krb5_tgs_rep
decode_krb5_tgs_req
decode_krb5_ticket
decode_krb5_typed_data
+encode_krb5_ad_kdcissued
encode_krb5_alt_method
encode_krb5_ap_rep
encode_krb5_ap_rep_enc_part
@@ -108,6 +110,7 @@ krb5_appdefault_string
krb5_auth_con_free
krb5_auth_con_genaddrs
krb5_auth_con_get_checksum_func
+krb5_auth_con_get_authdata_context
krb5_auth_con_getaddrs
krb5_auth_con_getauthenticator
krb5_auth_con_getflags
@@ -123,6 +126,7 @@ krb5_auth_con_getremotesubkey
krb5_auth_con_getsendsubkey
krb5_auth_con_init
krb5_auth_con_initivector
+krb5_auth_con_set_authdata_context
krb5_auth_con_set_checksum_func
krb5_auth_con_set_req_cksumtype
krb5_auth_con_set_safe_cksumtype
@@ -136,6 +140,18 @@ krb5_auth_con_setrecvsubkey
krb5_auth_con_setsendsubkey
krb5_auth_con_setuseruserkey
krb5_auth_to_rep
+krb5_authdata_context_copy
+krb5_authdata_context_free
+krb5_authdata_context_init
+krb5_authdata_delete_attribute
+krb5_authdata_get_attribute_types
+krb5_authdata_get_attribute
+krb5_authdata_set_attribute
+krb5_authdata_export_attributes
+krb5_authdata_export_authdata
+krb5_authdata_export_internal
+krb5_authdata_free_internal
+krb5_authdata_import_attributes
krb5_build_principal
krb5_build_principal_alloc_va
krb5_build_principal_ext
@@ -203,6 +219,7 @@ krb5_externalize_data
krb5_externalize_opaque
krb5_fcc_ops
krb5_find_serializer
+krb5_free_ad_kdcissued
krb5_free_address
krb5_free_addresses
krb5_free_alt_method
@@ -364,6 +381,7 @@ krb5_kuserok
krb5_libdefault_boolean
krb5_locate_kdc
krb5_lock_file
+krb5_make_authdata_kdc_issued
krb5_make_full_ipaddr
krb5_make_fulladdr
krb5_max_dgram_size
@@ -519,6 +537,7 @@ krb5_unparse_name_flags_ext
krb5_us_timeofday
krb5_use_natural_time
krb5_validate_times
+krb5_verify_authdata_kdc_issued
krb5_verify_init_creds
krb5_verify_init_creds_opt_init
krb5_verify_init_creds_opt_set_ap_req_nofail
@@ -534,6 +553,7 @@ krb5int_find_authdata
krb5int_find_pa_data
krb5int_foreach_localaddr
krb5int_free_addrlist
+krb5int_free_data_list
krb5int_get_domain_realm_mapping
krb5int_init_context_kdc
krb5int_initialize_library