/* -*- 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[] = { &krb5int_mspac_authdata_client_ftable, &krb5int_s4u2proxy_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 = 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 (kcontext == 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); 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 = 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); assert(code == 0 || *kdc_issued_authdata == NULL); krb5_free_authdata(kcontext, 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 && (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 (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; } 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); } 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); }