From 17ffdd0e93271072369e479f440ddf85e020580a Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Fri, 9 Oct 2009 18:29:34 +0000 Subject: 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 --- src/lib/krb5/asn.1/asn1_k_decode.c | 26 +- src/lib/krb5/asn.1/asn1_k_decode.h | 6 + src/lib/krb5/asn.1/asn1_k_encode.c | 19 +- src/lib/krb5/asn.1/krb5_decode.c | 11 + src/lib/krb5/ccache/cc_file.c | 4 +- src/lib/krb5/ccache/ccfns.c | 4 +- src/lib/krb5/error_tables/kv5m_err.et | 1 + src/lib/krb5/krb/Makefile.in | 11 +- src/lib/krb5/krb/auth_con.c | 20 + src/lib/krb5/krb/auth_con.h | 1 + src/lib/krb5/krb/authdata.c | 1245 +++++++++++++++++++++++++++++++++ src/lib/krb5/krb/authdata.h | 48 ++ src/lib/krb5/krb/copy_auth.c | 123 ++++ src/lib/krb5/krb/gc_frm_kdc.c | 31 +- src/lib/krb5/krb/int-proto.h | 1 + src/lib/krb5/krb/kfree.c | 30 +- src/lib/krb5/krb/mk_req_ext.c | 40 +- src/lib/krb5/krb/pac.c | 730 ++++++++++++++++++- src/lib/krb5/krb/rd_req.c | 22 +- src/lib/krb5/krb/rd_req_dec.c | 69 +- src/lib/krb5/krb/s4u_creds.c | 2 +- src/lib/krb5/krb/ser_actx.c | 2 + src/lib/krb5/krb/t_authdata.c | 16 +- src/lib/krb5/libkrb5.exports | 20 + 24 files changed, 2396 insertions(+), 86 deletions(-) create mode 100644 src/lib/krb5/krb/authdata.c create mode 100644 src/lib/krb5/krb/authdata.h (limited to 'src/lib/krb5') 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 + +/* 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 -- cgit