diff options
author | Sam Hartman <hartmans@mit.edu> | 2009-01-03 23:19:42 +0000 |
---|---|---|
committer | Sam Hartman <hartmans@mit.edu> | 2009-01-03 23:19:42 +0000 |
commit | 0ba5ccd7bb3ea15e44a87f84ca6feed8890f657d (patch) | |
tree | 2049c9c2cb135fe36b14c0a171711259258d18ec /src/kdc/kdc_authdata.c | |
parent | ff0a6514c9f4230938c29922d69cbd4e83691adf (diff) | |
download | krb5-0ba5ccd7bb3ea15e44a87f84ca6feed8890f657d.zip krb5-0ba5ccd7bb3ea15e44a87f84ca6feed8890f657d.tar.gz krb5-0ba5ccd7bb3ea15e44a87f84ca6feed8890f657d.tar.bz2 |
Merge mskrb-integ onto trunk
The mskrb-integ branch includes support for the following projects:
Projects/Aliases
* Projects/PAC and principal APIs
* Projects/AEAD encryption API
* Projects/GSSAPI DCE
* Projects/RFC 3244
In addition, it includes support for enctype negotiation, and a variety of GSS-API extensions.
In the KDC it includes support for protocol transition, constrained delegation
and a new authorization data interface.
The old authorization data interface is also supported.
This commit merges the mskrb-integ branch on to the trunk.
Additional review and testing is required.
Merge commit 'mskrb-integ' into trunk
ticket: new
status: open
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@21690 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/kdc/kdc_authdata.c')
-rw-r--r-- | src/kdc/kdc_authdata.c | 539 |
1 files changed, 427 insertions, 112 deletions
diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c index 9fd37f2..315269c 100644 --- a/src/kdc/kdc_authdata.c +++ b/src/kdc/kdc_authdata.c @@ -2,6 +2,7 @@ * kdc/kdc_authdata.c * * Copyright (C) 2007 Apple Inc. All Rights Reserved. + * Copyright (C) 2008 by the Massachusetts Institute of Technology. * * Export of this software from the United States of America may * require a specific license from the United States Government. @@ -42,109 +43,95 @@ static const char *objdirs[] = { KRB5_AUTHDATA_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/ static const char *objdirs[] = { LIBDIR "/krb5/plugins/authdata", NULL }; #endif -typedef krb5_error_code (*authdata_proc) +/* MIT Kerberos 1.6 (V0) authdata plugin callback */ +typedef krb5_error_code (*authdata_proc_0) (krb5_context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part * enc_tkt_reply); - +/* MIT Kerberos 1.7 (V1) authdata plugin callback */ +typedef krb5_error_code (*authdata_proc_1) + (krb5_context, unsigned int flags, + krb5_db_entry *client, krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_const_principal for_user_princ, + krb5_enc_tkt_part *enc_tkt_request, + krb5_enc_tkt_part *enc_tkt_reply); typedef krb5_error_code (*init_proc) (krb5_context, void **); typedef void (*fini_proc) (krb5_context, void *); +/* Internal authdata system for copying TGS-REQ authdata to ticket */ +static krb5_error_code handle_request_authdata + (krb5_context context, + unsigned int flags, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_const_principal for_user_princ, + krb5_enc_tkt_part *enc_tkt_request, + krb5_enc_tkt_part *enc_tkt_reply); + +/* Internal authdata system for handling KDC-issued authdata */ +static krb5_error_code handle_tgt_authdata + (krb5_context context, + unsigned int flags, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_const_principal for_user_princ, + krb5_enc_tkt_part *enc_tkt_request, + krb5_enc_tkt_part *enc_tkt_reply); + typedef struct _krb5_authdata_systems { const char *name; +#define AUTHDATA_SYSTEM_UNKNOWN -1 +#define AUTHDATA_SYSTEM_V0 0 +#define AUTHDATA_SYSTEM_V1 1 int type; +#define AUTHDATA_FLAG_CRITICAL 0x1 int flags; void *plugin_context; init_proc init; fini_proc fini; - authdata_proc handle_authdata; + union { + authdata_proc_1 v1; + authdata_proc_0 v0; + } handle_authdata; } krb5_authdata_systems; -#undef GREET_PREAUTH - -#ifdef GREET_PREAUTH -static krb5_error_code -greet_init(krb5_context ctx, void **blob) -{ - *blob = "hello"; - return 0; -} - -static void -greet_fini(krb5_context ctx, void *blob) -{ -} - -static krb5_error_code -greet_authdata(krb5_context ctx, krb5_db_entry *client, - krb5_data *req_pkt, - krb5_kdc_req *request, - krb5_enc_tkt_part * enc_tkt_reply) -{ -#define GREET_SIZE (20) - - char *p; - krb5_authdata *a; - size_t count; - krb5_authdata **new_ad; - - krb5_klog_syslog (LOG_DEBUG, "in greet_authdata"); - - p = calloc(1, GREET_SIZE); - a = calloc(1, sizeof(*a)); - - if (p == NULL || a == NULL) { - free(p); - free(a); - return ENOMEM; - } - strlcpy(p, "hello", GREET_SIZE); - a->magic = KV5M_AUTHDATA; - a->ad_type = -42; - a->length = GREET_SIZE; - a->contents = p; - if (enc_tkt_reply->authorization_data == 0) { - count = 0; - } else { - for (count = 0; enc_tkt_reply->authorization_data[count] != 0; count++) - ; - } - new_ad = realloc(enc_tkt_reply->authorization_data, - (count+2) * sizeof(krb5_authdata *)); - if (new_ad == NULL) { - free(p); - free(a); - return ENOMEM; - } - enc_tkt_reply->authorization_data = new_ad; - new_ad[count] = a; - new_ad[count+1] = NULL; - return 0; -} -#endif - static krb5_authdata_systems static_authdata_systems[] = { -#ifdef GREET_PREAUTH - { "greeting", 0, 0, 0, greet_init, greet_fini, greet_authdata }, -#endif - { "[end]", -1,} + { "tgs_req", AUTHDATA_SYSTEM_V1, AUTHDATA_FLAG_CRITICAL, NULL, NULL, NULL, { handle_request_authdata } }, + { "tgt", AUTHDATA_SYSTEM_V1, AUTHDATA_FLAG_CRITICAL, NULL, NULL, NULL, { handle_tgt_authdata } }, }; static krb5_authdata_systems *authdata_systems; static int n_authdata_systems; static struct plugin_dir_handle authdata_plugins; +/* Load both v0 and v1 authdata plugins */ krb5_error_code load_authdata_plugins(krb5_context context) { - void **authdata_plugins_ftables = NULL; - struct krb5plugin_authdata_ftable_v0 *ftable = NULL; + void **authdata_plugins_ftables_v0 = NULL; + void **authdata_plugins_ftables_v1 = NULL; size_t module_count; - int i, k; + size_t i, k; init_proc server_init_proc = NULL; + krb5_error_code code; /* Attempt to load all of the authdata plugins we can find. */ PLUGIN_DIR_INIT(&authdata_plugins); @@ -156,23 +143,41 @@ load_authdata_plugins(krb5_context context) } /* Get the method tables provided by the loaded plugins. */ - authdata_plugins_ftables = NULL; + authdata_plugins_ftables_v0 = NULL; + authdata_plugins_ftables_v1 = NULL; n_authdata_systems = 0; + if (krb5int_get_plugin_dir_data(&authdata_plugins, + "authdata_server_1", + &authdata_plugins_ftables_v1, &context->err) != 0 || + krb5int_get_plugin_dir_data(&authdata_plugins, "authdata_server_0", - &authdata_plugins_ftables, &context->err) != 0) { - return KRB5_PLUGIN_NO_HANDLE; + &authdata_plugins_ftables_v0, &context->err) != 0) { + code = KRB5_PLUGIN_NO_HANDLE; + goto cleanup; } /* Count the valid modules. */ module_count = sizeof(static_authdata_systems) / sizeof(static_authdata_systems[0]); - if (authdata_plugins_ftables != NULL) { - for (i = 0; authdata_plugins_ftables[i] != NULL; i++) { - ftable = authdata_plugins_ftables[i]; - if ((ftable->authdata_proc != NULL)) { + + if (authdata_plugins_ftables_v1 != NULL) { + struct krb5plugin_authdata_ftable_v1 *ftable; + + for (i = 0; authdata_plugins_ftables_v1[i] != NULL; i++) { + ftable = authdata_plugins_ftables_v1[i]; + if (ftable->authdata_proc != NULL) + module_count++; + } + } + + if (authdata_plugins_ftables_v0 != NULL) { + struct krb5plugin_authdata_ftable_v0 *ftable; + + for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) { + ftable = authdata_plugins_ftables_v0[i]; + if (ftable->authdata_proc != NULL) module_count++; - } } } @@ -180,16 +185,14 @@ load_authdata_plugins(krb5_context context) * leave room for a terminator entry. */ authdata_systems = calloc(module_count + 1, sizeof(krb5_authdata_systems)); if (authdata_systems == NULL) { - krb5int_free_plugin_dir_data(authdata_plugins_ftables); - return ENOMEM; + code = ENOMEM; + goto cleanup; } /* Add the locally-supplied mechanisms to the dynamic list first. */ for (i = 0, k = 0; i < sizeof(static_authdata_systems) / sizeof(static_authdata_systems[0]); i++) { - if (static_authdata_systems[i].type == -1) - break; authdata_systems[k] = static_authdata_systems[i]; /* Try to initialize the authdata system. If it fails, we'll remove it * from the list of systems we'll be using. */ @@ -202,13 +205,53 @@ load_authdata_plugins(krb5_context context) k++; } - /* Now add the dynamically-loaded mechanisms to the list. */ - if (authdata_plugins_ftables != NULL) { - for (i = 0; authdata_plugins_ftables[i] != NULL; i++) { + /* Add dynamically loaded V1 plugins */ + if (authdata_plugins_ftables_v1 != NULL) { + struct krb5plugin_authdata_ftable_v1 *ftable; + + for (i = 0; authdata_plugins_ftables_v1[i] != NULL; i++) { + krb5_error_code initerr; + void *pctx = NULL; + + ftable = authdata_plugins_ftables_v1[i]; + if ((ftable->authdata_proc == NULL)) { + continue; + } + server_init_proc = ftable->init_proc; + if ((server_init_proc != NULL) && + ((initerr = (*server_init_proc)(context, &pctx)) != 0)) { + const char *emsg; + emsg = krb5_get_error_message(context, initerr); + if (emsg) { + krb5_klog_syslog(LOG_ERR, + "authdata %s failed to initialize: %s", + ftable->name, emsg); + krb5_free_error_message(context, emsg); + } + memset(&authdata_systems[k], 0, sizeof(authdata_systems[k])); + + continue; + } + + authdata_systems[k].name = ftable->name; + authdata_systems[k].type = AUTHDATA_SYSTEM_V1; + authdata_systems[k].init = server_init_proc; + authdata_systems[k].fini = ftable->fini_proc; + authdata_systems[k].handle_authdata.v1 = ftable->authdata_proc; + authdata_systems[k].plugin_context = pctx; + k++; + } + } + + /* Add dynamically loaded V0 plugins */ + if (authdata_plugins_ftables_v0 != NULL) { + struct krb5plugin_authdata_ftable_v0 *ftable; + + for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) { krb5_error_code initerr; void *pctx = NULL; - ftable = authdata_plugins_ftables[i]; + ftable = authdata_plugins_ftables_v0[i]; if ((ftable->authdata_proc == NULL)) { continue; } @@ -229,19 +272,28 @@ load_authdata_plugins(krb5_context context) } authdata_systems[k].name = ftable->name; + authdata_systems[k].type = AUTHDATA_SYSTEM_V0; authdata_systems[k].init = server_init_proc; authdata_systems[k].fini = ftable->fini_proc; - authdata_systems[k].handle_authdata = ftable->authdata_proc; + authdata_systems[k].handle_authdata.v0 = ftable->authdata_proc; authdata_systems[k].plugin_context = pctx; k++; } - krb5int_free_plugin_dir_data(authdata_plugins_ftables); } + n_authdata_systems = k; /* Add the end-of-list marker. */ authdata_systems[k].name = "[end]"; - authdata_systems[k].type = -1; - return 0; + authdata_systems[k].type = AUTHDATA_SYSTEM_UNKNOWN; + code = 0; + +cleanup: + if (authdata_plugins_ftables_v1 != NULL) + krb5int_free_plugin_dir_data(authdata_plugins_ftables_v1); + if (authdata_plugins_ftables_v0 != NULL) + krb5int_free_plugin_dir_data(authdata_plugins_ftables_v0); + + return code; } krb5_error_code @@ -264,33 +316,296 @@ unload_authdata_plugins(krb5_context context) return 0; } +/* Merge authdata. If copy == 0, in_authdata is invalid on return */ +static krb5_error_code +merge_authdata (krb5_context context, + krb5_authdata **in_authdata, + krb5_authdata ***out_authdata, + krb5_boolean copy) +{ + size_t i, nadata = 0; + krb5_authdata **authdata = *out_authdata; + + if (in_authdata == NULL || in_authdata[0] == NULL) + return 0; + + if (authdata != NULL) { + for (nadata = 0; authdata[nadata] != NULL; nadata++) + ; + } + + for (i = 0; in_authdata[i] != NULL; i++) + ; + + if (authdata == NULL) { + authdata = (krb5_authdata **)calloc(i + 1, sizeof(krb5_authdata *)); + } else { + authdata = (krb5_authdata **)realloc(authdata, + ((nadata + i + 1) * sizeof(krb5_authdata *))); + } + if (authdata == NULL) + return ENOMEM; + + if (copy) { + krb5_error_code code; + krb5_authdata **tmp; + + code = krb5_copy_authdata(context, in_authdata, &tmp); + if (code != 0) + return code; + + in_authdata = tmp; + } + + for (i = 0; in_authdata[i] != NULL; i++) + authdata[nadata + i] = in_authdata[i]; + + authdata[nadata + i] = NULL; + + free(in_authdata); + + *out_authdata = authdata; + + return 0; +} + +/* Handle copying TGS-REQ authorization data into reply */ +static krb5_error_code +handle_request_authdata (krb5_context context, + unsigned int flags, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_const_principal for_user_princ, + krb5_enc_tkt_part *enc_tkt_request, + krb5_enc_tkt_part *enc_tkt_reply) +{ + krb5_error_code code; + krb5_data scratch; + + if (request->msg_type != KRB5_TGS_REQ || + request->authorization_data.ciphertext.data == NULL) + return 0; + + assert(enc_tkt_request != NULL); + + scratch.length = request->authorization_data.ciphertext.length; + scratch.data = malloc(scratch.length); + if (scratch.data == NULL) + return ENOMEM; + + code = krb5_c_decrypt(context, + enc_tkt_request->session, + KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, + 0, &request->authorization_data, + &scratch); + if (code != 0) { + free(scratch.data); + return code; + } + + /* scratch now has the authorization data, so we decode it, and make + * it available to subsequent authdata plugins */ + code = decode_krb5_authdata(&scratch, &request->unenc_authdata); + if (code != 0) { + free(scratch.data); + return code; + } + + free(scratch.data); + + code = merge_authdata(context, request->unenc_authdata, + &enc_tkt_reply->authorization_data, TRUE /* copy */); + + return code; +} + +/* Handle backend-managed authorization data */ +static krb5_error_code +handle_tgt_authdata (krb5_context context, + unsigned int flags, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_const_principal for_user_princ, + krb5_enc_tkt_part *enc_tkt_request, + krb5_enc_tkt_part *enc_tkt_reply) +{ + krb5_error_code code; + krb5_authdata **db_authdata = NULL; + krb5_db_entry ad_entry; + int ad_nprincs = 0; + krb5_boolean tgs_req = (request->msg_type == KRB5_TGS_REQ); + krb5_const_principal actual_client; + + /* + * Check whether KDC issued authorization data should be included. + * A server can explicitly disable the inclusion of authorization + * data by setting the KRB5_KDB_NO_AUTH_DATA_REQUIRED flag on its + * principal entry. Otherwise authorization data will be included + * if it was present in the TGT, the client is from another realm + * or protocol transition/constrained delegation was used, or, in + * the AS-REQ case, if the pre-auth data indicated the PAC should + * be present. + * + * We permit sign_authorization_data() to return a krb5_db_entry + * representing the principal associated with the authorization + * data, in case that principal is not local to our realm and we + * need to perform additional checks (such as disabling delegation + * for cross-realm protocol transition below). + */ + if (tgs_req) { + assert(enc_tkt_request != NULL); + + if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED)) + return 0; + + if (enc_tkt_request->authorization_data == NULL && + !isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM | KRB5_KDB_FLAGS_S4U)) + return 0; + + assert(enc_tkt_reply->times.authtime == enc_tkt_request->times.authtime); + } else { + if (!isflagset(flags, KRB5_KDB_FLAG_INCLUDE_PAC)) + return 0; + } + + /* + * We have this special case for protocol transition, because for + * cross-realm protocol transition the ticket reply client will + * not be changed until the final hop. + */ + if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) + actual_client = for_user_princ; + else + actual_client = enc_tkt_reply->client; + + /* + * If the backend does not implement the sign authdata method, then + * just copy the TGT authorization data into the reply, except for + * the constrained delegation case (which requires special handling + * because it will promote untrusted auth data to KDC issued auth + * data; this requires backend-specific code) + * + * Presently this interface does not support using request auth data + * to influence (eg. possibly restrict) the reply auth data. + */ + code = sign_db_authdata(context, + flags, + actual_client, + client, + server, + krbtgt, + client_key, + server_key, /* U2U or server key */ + enc_tkt_reply->times.authtime, + tgs_req ? enc_tkt_request->authorization_data : NULL, + &db_authdata, + &ad_entry, + &ad_nprincs); + if (code == KRB5_KDB_DBTYPE_NOSUP) { + assert(ad_nprincs == 0); + assert(db_authdata == NULL); + + if (isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) + return KRB5KDC_ERR_POLICY; + + if (tgs_req) + return merge_authdata(context, enc_tkt_request->authorization_data, + &enc_tkt_reply->authorization_data, TRUE); + else + return 0; + } + + if (ad_nprincs != 0) { + if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && + isflagset(ad_entry.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) + clear(enc_tkt_reply->flags, TKT_FLG_FORWARDABLE); + + krb5_db_free_principal(context, &ad_entry, ad_nprincs); + + if (ad_nprincs != 1) { + if (db_authdata != NULL) + krb5_free_authdata(context, db_authdata); + return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; + } + } + + if (db_authdata != NULL) { + code = merge_authdata(context, db_authdata, + &enc_tkt_reply->authorization_data, + FALSE); + if (code != 0) + krb5_free_authdata(context, db_authdata); + } + + return code; +} + krb5_error_code -handle_authdata (krb5_context context, krb5_db_entry *client, - krb5_data *req_pkt, krb5_kdc_req *request, +handle_authdata (krb5_context context, + unsigned int flags, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_const_principal for_user_princ, + krb5_enc_tkt_part *enc_tkt_request, krb5_enc_tkt_part *enc_tkt_reply) { - krb5_error_code retval = 0; + krb5_error_code code = 0; int i; - const char *emsg; - - krb5_klog_syslog (LOG_DEBUG, "handling authdata"); + assert(enc_tkt_reply->authorization_data == NULL); for (i = 0; i < n_authdata_systems; i++) { const krb5_authdata_systems *asys = &authdata_systems[i]; - if (asys->handle_authdata && asys->type != -1) { - retval = asys->handle_authdata(context, client, req_pkt, - request, enc_tkt_reply); - if (retval) { - emsg = krb5_get_error_message (context, retval); - krb5_klog_syslog (LOG_INFO, - "authdata (%s) handling failure: %s", - asys->name, emsg); - krb5_free_error_message (context, emsg); - } else { - krb5_klog_syslog (LOG_DEBUG, ".. .. ok"); - } + + switch (asys->type) { + case AUTHDATA_SYSTEM_V0: + /* V0 was only in AS-REQ code path */ + if (request->msg_type != KRB5_AS_REQ) + continue; + + code = (*asys->handle_authdata.v0)(context, client, req_pkt, + request, enc_tkt_reply); + break; + case AUTHDATA_SYSTEM_V1: + code = (*asys->handle_authdata.v1)(context, flags, + client, server, krbtgt, + client_key, server_key, + req_pkt, request, for_user_princ, + enc_tkt_request, + enc_tkt_reply); + break; + default: + code = 0; + break; + } + if (code != 0) { + const char *emsg; + + emsg = krb5_get_error_message (context, code); + krb5_klog_syslog (LOG_INFO, + "authdata (%s) handling failure: %s", + asys->name, emsg); + krb5_free_error_message (context, emsg); + + if (asys->flags & AUTHDATA_FLAG_CRITICAL) + break; } } - return 0; + return code; } + |