diff options
Diffstat (limited to 'src/lib/krb5/krb/authdata.c')
| -rw-r--r-- | src/lib/krb5/krb/authdata.c | 1311 |
1 files changed, 1311 insertions, 0 deletions
diff --git a/src/lib/krb5/krb/authdata.c b/src/lib/krb5/krb/authdata.c new file mode 100644 index 0000000000000..abb2ab9e255e8 --- /dev/null +++ b/src/lib/krb5/krb/authdata.c @@ -0,0 +1,1311 @@ +/* -*- mode: c; c-basic-offset: 4; 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[] = { + &k5_mspac_ad_client_ftable, + &k5_s4u2proxy_ad_client_ftable, + &k5_authind_ad_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 = NULL; + + 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 (!data_eq_string(*name, module->name)) + 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 (context == NULL) { + code = ENOMEM; + goto cleanup; + } + context->magic = KV5M_AUTHDATA_CONTEXT; + context->modules = calloc(n_modules, sizeof(context->modules[0])); + if (context->modules == NULL) { + code = ENOMEM; + goto cleanup; + } + 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) + goto cleanup; + } + + for (i = 0; i < internal_count; i++) { + code = k5_ad_init_modules(kcontext, context, authdata_systems[i], &k); + if (code != 0) + goto cleanup; + } + + context->plugins = plugins; + +cleanup: + if (tables != NULL) + krb5int_free_plugin_dir_data(tables); + + if (code != 0) { + krb5int_close_plugin_dirs(&plugins); + krb5_authdata_context_free(kcontext, context); + } else { + /* plugins is owned by context now */ + *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); + zapfree(context, sizeof(*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); +} + +/* Return 0 with *kdc_issued_authdata == NULL on verification failure. */ +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 = krb5_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); + + if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY || + code == KRB5KRB_AP_ERR_INAPP_CKSUM || + code == KRB5_BAD_ENCTYPE || code == KRB5_BAD_MSIZE) + code = 0; + + krb5_free_authdata(kcontext, authdata); + + return code; +} + +/* Decode and verify each CAMMAC and collect the resulting authdata, + * ignoring those that failed verification. */ +static krb5_error_code +extract_cammacs(krb5_context kcontext, krb5_authdata **cammacs, + const krb5_keyblock *key, krb5_authdata ***ad_out) +{ + krb5_error_code ret; + krb5_authdata **list = NULL, **elements = NULL, **new_list; + size_t i, n_elements, count = 0; + + *ad_out = NULL; + + for (i = 0; cammacs != NULL && cammacs[i] != NULL; i++) { + ret = k5_unwrap_cammac_svc(kcontext, cammacs[i], key, &elements); + if (ret && ret != KRB5KRB_AP_ERR_BAD_INTEGRITY) + goto cleanup; + ret = 0; + + /* Add the verified elements to list and free the container array. */ + for (n_elements = 0; elements[n_elements] != NULL; n_elements++); + new_list = realloc(list, (count + n_elements + 1) * sizeof(*list)); + if (new_list == NULL) { + ret = ENOMEM; + goto cleanup; + } + list = new_list; + memcpy(list + count, elements, n_elements * sizeof(*list)); + count += n_elements; + list[count] = NULL; + free(elements); + elements = NULL; + } + + *ad_out = list; + list = NULL; + +cleanup: + krb5_free_authdata(kcontext, list); + krb5_free_authdata(kcontext, elements); + return ret; +} + +/* Retrieve verified CAMMAC contained elements. */ +static krb5_error_code +get_cammac_authdata(krb5_context kcontext, const krb5_ap_req *ap_req, + const krb5_keyblock *key, krb5_authdata ***elems_out) +{ + krb5_error_code ret = 0; + krb5_authdata **ticket_authdata, **cammacs, **elements; + + *elems_out = NULL; + + ticket_authdata = ap_req->ticket->enc_part2->authorization_data; + ret = krb5_find_authdata(kcontext, ticket_authdata, NULL, + KRB5_AUTHDATA_CAMMAC, &cammacs); + if (ret || cammacs == NULL) + return ret; + + ret = extract_cammacs(kcontext, cammacs, key, &elements); + if (!ret) + *elems_out = elements; + + krb5_free_authdata(kcontext, cammacs); + return ret; +} + +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; + krb5_authdata **cammac_authdata = NULL; + + authen_authdata = (*auth_context)->authentp->authorization_data; + ticket_authdata = ap_req->ticket->enc_part2->authorization_data; + + code = k5_get_kdc_issued_authdata(kcontext, ap_req, &kdc_issuer, + &kdc_issued_authdata); + if (code) + goto cleanup; + + code = get_cammac_authdata(kcontext, ap_req, key, &cammac_authdata); + if (code) + goto cleanup; + + 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 && + (module->flags & AD_USAGE_KDC_ISSUED)) { + code = krb5_find_authdata(kcontext, kdc_issued_authdata, NULL, + module->ad_type, &authdata); + if (code != 0) + break; + + kdc_issued_flag = TRUE; + } + + if (cammac_authdata != NULL && (module->flags & AD_CAMMAC_PROTECTED)) { + code = krb5_find_authdata(kcontext, cammac_authdata, NULL, + module->ad_type, &authdata); + if (code) + break; + + kdc_issued_flag = TRUE; + } + + if (authdata == NULL) { + krb5_boolean ticket_usage = FALSE; + krb5_boolean authen_usage = FALSE; + + /* + * Determine which authdata sources to interrogate based on the + * module's usage. This is important if the authdata is signed + * by the KDC with the TGT key (as the user can forge that in + * the AP-REQ). + */ + if (module->flags & (AD_USAGE_AS_REQ | AD_USAGE_TGS_REQ)) + ticket_usage = TRUE; + if (module->flags & AD_USAGE_AP_REQ) + authen_usage = TRUE; + + code = krb5_find_authdata(kcontext, + ticket_usage ? ticket_authdata : NULL, + authen_usage ? authen_authdata : NULL, + 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; + } + +cleanup: + krb5_free_principal(kcontext, kdc_issuer); + krb5_free_authdata(kcontext, kdc_issued_authdata); + krb5_free_authdata(kcontext, cammac_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); +} + +krb5_error_code +krb5int_copy_authdatum(krb5_context context, + const krb5_authdata *inad, krb5_authdata **outad) +{ + krb5_authdata *tmpad; + + if (!(tmpad = (krb5_authdata *)malloc(sizeof(*tmpad)))) + return ENOMEM; + *tmpad = *inad; + if (!(tmpad->contents = (krb5_octet *)malloc(inad->length))) { + free(tmpad); + return ENOMEM; + } + memcpy(tmpad->contents, inad->contents, inad->length); + *outad = tmpad; + return 0; +} + +void KRB5_CALLCONV +krb5_free_authdata(krb5_context context, krb5_authdata **val) +{ + register krb5_authdata **temp; + + if (val == NULL) + return; + for (temp = val; *temp; temp++) { + free((*temp)->contents); + free(*temp); + } + free(val); +} |
