diff options
| author | Sam Hartman <hartmans@mit.edu> | 2009-12-28 17:15:30 +0000 |
|---|---|---|
| committer | Sam Hartman <hartmans@mit.edu> | 2009-12-28 17:15:30 +0000 |
| commit | ec49e6e673ab229462ef18aa2986167eaa643643 (patch) | |
| tree | 625dba55e939a0073cf69f7b79c8c0010df991eb /src/lib | |
| parent | c5479d0c5b29430a49cf3683513c1223a173ac4e (diff) | |
| download | krb5-ec49e6e673ab229462ef18aa2986167eaa643643.tar.gz krb5-ec49e6e673ab229462ef18aa2986167eaa643643.tar.xz krb5-ec49e6e673ab229462ef18aa2986167eaa643643.zip | |
Anonymous support for Kerberos
This ticket implements Project/Anonymous pkinit from k5wiki. Provides
support for completely anonymous principals and untested client
support for realm-exposed anonymous authentication.
* Introduce kinit -n
* Introduce kadmin -n
* krb5_get_init_creds_opt_set_out_ccache aliases the supplied ccache
* No longer generate ad-initial-verified-cas in pkinit
* Fix pkinit interactions with non-TGT authentication
Merge remote branch 'anonymous' into trunk
Conflicts:
src/lib/krb5/krb/gic_opt.c
ticket: 6607
Tags: enhancement
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23527 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/gssapi/krb5/disp_name.c | 9 | ||||
| -rw-r--r-- | src/lib/gssapi/krb5/import_name.c | 11 | ||||
| -rw-r--r-- | src/lib/kadm5/admin.h | 7 | ||||
| -rw-r--r-- | src/lib/kadm5/clnt/client_init.c | 60 | ||||
| -rw-r--r-- | src/lib/kadm5/clnt/libkadm5clnt.exports | 1 | ||||
| -rw-r--r-- | src/lib/kadm5/srv/libkadm5srv.exports | 1 | ||||
| -rw-r--r-- | src/lib/kadm5/srv/server_init.c | 13 | ||||
| -rw-r--r-- | src/lib/krb5/krb/bld_princ.c | 30 | ||||
| -rw-r--r-- | src/lib/krb5/krb/chk_trans.c | 6 | ||||
| -rw-r--r-- | src/lib/krb5/krb/get_in_tkt.c | 101 | ||||
| -rw-r--r-- | src/lib/krb5/krb/gic_opt.c | 20 | ||||
| -rw-r--r-- | src/lib/krb5/libkrb5.exports | 3 |
12 files changed, 228 insertions, 34 deletions
diff --git a/src/lib/gssapi/krb5/disp_name.c b/src/lib/gssapi/krb5/disp_name.c index ac576f5b4..79b14f1a9 100644 --- a/src/lib/gssapi/krb5/disp_name.c +++ b/src/lib/gssapi/krb5/disp_name.c @@ -34,6 +34,8 @@ krb5_gss_display_name(minor_status, input_name, output_name_buffer, krb5_context context; krb5_error_code code; char *str; + krb5_gss_name_t k5name = (krb5_gss_name_t) input_name; + gss_OID nametype = (gss_OID) gss_nt_krb5_name; code = krb5_gss_init_context(&context); if (code) { @@ -49,6 +51,11 @@ krb5_gss_display_name(minor_status, input_name, output_name_buffer, krb5_free_context(context); return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); } + if (krb5_princ_type(context, k5name->princ) == KRB5_NT_WELLKNOWN) { + if (krb5_principal_compare(context, k5name->princ, + krb5_anonymous_principal())) + nametype = GSS_C_NT_ANONYMOUS; + } if ((code = krb5_unparse_name(context, ((krb5_gss_name_t) input_name)->princ, @@ -72,6 +79,6 @@ krb5_gss_display_name(minor_status, input_name, output_name_buffer, *minor_status = 0; if (output_name_type) - *output_name_type = (gss_OID) gss_nt_krb5_name; + *output_name_type = (gss_OID) nametype; return(GSS_S_COMPLETE); } diff --git a/src/lib/gssapi/krb5/import_name.c b/src/lib/gssapi/krb5/import_name.c index cd2748b56..cfb75fb22 100644 --- a/src/lib/gssapi/krb5/import_name.c +++ b/src/lib/gssapi/krb5/import_name.c @@ -154,7 +154,16 @@ krb5_gss_import_name(minor_status, input_name_buffer, krb5_free_context(context); return(GSS_S_FAILURE); } - } else { + } else if ((input_name_type != NULL) && + g_OID_equal(input_name_type, GSS_C_NT_ANONYMOUS)) { + code = krb5_copy_principal(context, krb5_anonymous_principal(), &princ); + if (code != 0) { + krb5_free_context(context); + *minor_status = code; + return GSS_S_FAILURE; + } + } + else { #ifndef NO_PASSWORD uid_t uid; struct passwd pwx; diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h index 4196a19e2..8fad11177 100644 --- a/src/lib/kadm5/admin.h +++ b/src/lib/kadm5/admin.h @@ -338,6 +338,13 @@ kadm5_ret_t kadm5_init(krb5_context context, char *client_name, krb5_ui_4 api_version, char **db_args, void **server_handle); +kadm5_ret_t kadm5_init_anonymous(krb5_context context, char *client_name, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + char **db_args, + void **server_handle); kadm5_ret_t kadm5_init_with_password(krb5_context context, char *client_name, char *pass, diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c index 95c4954ef..82033e9fd 100644 --- a/src/lib/kadm5/clnt/client_init.c +++ b/src/lib/kadm5/clnt/client_init.c @@ -59,7 +59,7 @@ #define ADM_CCACHE "/tmp/ovsec_adm.XXXXXX" -enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS }; +enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS , INIT_ANONYMOUS}; static kadm5_ret_t _kadm5_init_any(krb5_context context, char *client_name, @@ -129,6 +129,19 @@ kadm5_ret_t kadm5_init_with_password(krb5_context context, char *client_name, api_version, db_args, server_handle); } +kadm5_ret_t kadm5_init_anonymous(krb5_context context, char *client_name, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + char **db_args, + void **server_handle) +{ + return _kadm5_init_any(context, client_name, INIT_ANONYMOUS, NULL, NULL, + service_name, params, struct_version, + api_version, db_args, server_handle); +} + kadm5_ret_t kadm5_init(krb5_context context, char *client_name, char *pass, char *service_name, kadm5_config_params *params, @@ -343,7 +356,8 @@ static kadm5_ret_t _kadm5_init_any(krb5_context context, char *client_name, * The RPC connection is open; establish the GSS-API * authentication context. */ - code = kadm5_setup_gss(handle, params_in, client_name, full_svcname); + code = kadm5_setup_gss(handle, params_in, (init_type == INIT_CREDS)?client_name:NULL, + full_svcname); if (code) goto error; @@ -490,7 +504,7 @@ kadm5_get_init_creds(kadm5_server_handle_t handle, full_svcname, full_svcname_len); if ((code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || code == KRB5_CC_NOTFOUND) && svcname_in == NULL) { - /* Retry with old host-independent service princpal. */ + /* Retry with old host-independent service principal. */ code = kadm5_gic_iter(handle, init_type, ccache, client, pass, KADM5_ADMIN_SERVICE, realm, @@ -525,7 +539,7 @@ kadm5_gic_iter(kadm5_server_handle_t handle, kadm5_ret_t code; krb5_context ctx; krb5_keytab kt; - krb5_get_init_creds_opt opt; + krb5_get_init_creds_opt *opt = NULL; krb5_creds mcreds, outcreds; int n; @@ -540,29 +554,32 @@ kadm5_gic_iter(kadm5_server_handle_t handle, if (realm) { n = snprintf(full_svcname, full_svcname_len, "%s@%s", svcname, realm); - if (n < 0 || n >= full_svcname_len) + if (n < 0 || n >= (int) full_svcname_len) goto error; } else { /* krb5_princ_realm(client) is not null terminated */ n = snprintf(full_svcname, full_svcname_len, "%s@%.*s", svcname, krb5_princ_realm(ctx, client)->length, krb5_princ_realm(ctx, client)->data); - if (n < 0 || n >= full_svcname_len) + if (n < 0 || n >= (int) full_svcname_len) goto error; } /* Credentials for kadmin don't need to be forwardable or proxiable. */ if (init_type != INIT_CREDS) { - krb5_get_init_creds_opt_init(&opt); - krb5_get_init_creds_opt_set_forwardable(&opt, 0); - krb5_get_init_creds_opt_set_proxiable(&opt, 0); + code = krb5_get_init_creds_opt_alloc(ctx, &opt); + krb5_get_init_creds_opt_set_forwardable(opt, 0); + krb5_get_init_creds_opt_set_proxiable(opt, 0); + krb5_get_init_creds_opt_set_out_ccache(ctx, opt, ccache); + if (init_type == INIT_ANONYMOUS) + krb5_get_init_creds_opt_set_anonymous(opt, 1); } - if (init_type == INIT_PASS) { + if (init_type == INIT_PASS || init_type == INIT_ANONYMOUS) { code = krb5_get_init_creds_password(ctx, &outcreds, client, pass, krb5_prompter_posix, NULL, 0, - full_svcname, &opt); + full_svcname, opt); if (code) goto error; } else if (init_type == INIT_SKEY) { @@ -572,7 +589,7 @@ kadm5_gic_iter(kadm5_server_handle_t handle, goto error; } code = krb5_get_init_creds_keytab(ctx, &outcreds, client, kt, - 0, full_svcname, &opt); + 0, full_svcname, opt); if (pass) krb5_kt_close(ctx, kt); if (code) @@ -588,14 +605,10 @@ kadm5_gic_iter(kadm5_server_handle_t handle, if (code) goto error; } - if (init_type != INIT_CREDS) { - /* Caller has initialized ccache. */ - code = krb5_cc_store_cred(ctx, ccache, &outcreds); - if (code) - goto error; - } error: krb5_free_cred_contents(ctx, &outcreds); + if (opt) + krb5_get_init_creds_opt_free(ctx, opt); return code; } @@ -644,10 +657,13 @@ kadm5_setup_gss(kadm5_server_handle_t handle, goto error; } - buf.value = client_name; - buf.length = strlen((char *)buf.value) + 1; - gssstat = gss_import_name(&minor_stat, &buf, - (gss_OID) gss_nt_krb5_name, &gss_client); + if (client_name) { + buf.value = client_name; + buf.length = strlen((char *)buf.value) + 1; + gssstat = gss_import_name(&minor_stat, &buf, + (gss_OID) gss_nt_krb5_name, &gss_client); + } else gss_client = GSS_C_NO_NAME; + if (gssstat != GSS_S_COMPLETE) { code = KADM5_GSS_ERROR; goto error; diff --git a/src/lib/kadm5/clnt/libkadm5clnt.exports b/src/lib/kadm5/clnt/libkadm5clnt.exports index 617484778..5e81580b1 100644 --- a/src/lib/kadm5/clnt/libkadm5clnt.exports +++ b/src/lib/kadm5/clnt/libkadm5clnt.exports @@ -24,6 +24,7 @@ kadm5_get_principal kadm5_get_principals kadm5_get_privs kadm5_init +kadm5_init_anonymous kadm5_init_krb5_context kadm5_init_with_creds kadm5_init_with_password diff --git a/src/lib/kadm5/srv/libkadm5srv.exports b/src/lib/kadm5/srv/libkadm5srv.exports index 35745be88..d8d3b2283 100644 --- a/src/lib/kadm5/srv/libkadm5srv.exports +++ b/src/lib/kadm5/srv/libkadm5srv.exports @@ -40,6 +40,7 @@ kadm5_get_principal_keys kadm5_get_principals kadm5_get_privs kadm5_init +kadm5_init_anonymous kadm5_init_krb5_context kadm5_init_with_creds kadm5_init_with_password diff --git a/src/lib/kadm5/srv/server_init.c b/src/lib/kadm5/srv/server_init.c index ed71cbf96..557ef0ad4 100644 --- a/src/lib/kadm5/srv/server_init.c +++ b/src/lib/kadm5/srv/server_init.c @@ -104,6 +104,19 @@ kadm5_ret_t kadm5_init_with_password(krb5_context context, char *client_name, server_handle); } +kadm5_ret_t kadm5_init_anonymous(krb5_context context, char *client_name, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + char **db_args, + void **server_handle) +{ + return kadm5_init(context, client_name, NULL, service_name, params, + struct_version, api_version, db_args, + server_handle); +} + kadm5_ret_t kadm5_init_with_creds(krb5_context context, char *client_name, krb5_ccache ccache, diff --git a/src/lib/krb5/krb/bld_princ.c b/src/lib/krb5/krb/bld_princ.c index ac2c92a9e..8378599d3 100644 --- a/src/lib/krb5/krb/bld_princ.c +++ b/src/lib/krb5/krb/bld_princ.c @@ -187,3 +187,33 @@ krb5_build_principal(krb5_context context, return retval; } + +/*Anonymous and well known principals*/ +static const char anon_realm_str[] += KRB5_ANONYMOUS_REALMSTR; +static const krb5_data anon_realm_data = { + KV5M_DATA, sizeof(anon_realm_str)-1, + (char *) anon_realm_str}; +static const char wellknown_str[] = KRB5_WELLKNOWN_NAMESTR; +static const char anon_str[] = KRB5_ANONYMOUS_PRINCSTR; +static const krb5_data anon_princ_data[] = { + {KV5M_DATA, sizeof(wellknown_str)-1, (char *) wellknown_str}, + {KV5M_DATA, sizeof(anon_str)-1, (char *)anon_str} +}; + +const krb5_principal_data anon_princ = { + KV5M_PRINCIPAL, + {KV5M_DATA, sizeof(anon_realm_str)-1, (char *) anon_realm_str}, + (krb5_data *) anon_princ_data, 2, KRB5_NT_WELLKNOWN +}; + +const krb5_data * KRB5_CALLCONV +krb5_anonymous_realm() +{ + return &anon_realm_data; +} +krb5_const_principal KRB5_CALLCONV +krb5_anonymous_principal() +{ + return &anon_princ; +} diff --git a/src/lib/krb5/krb/chk_trans.c b/src/lib/krb5/krb/chk_trans.c index 3c014817c..def50885c 100644 --- a/src/lib/krb5/krb/chk_trans.c +++ b/src/lib/krb5/krb/chk_trans.c @@ -315,6 +315,7 @@ krb5_check_transited_list (krb5_context ctx, const krb5_data *trans_in, krb5_data trans; struct check_data cdata; krb5_error_code r; + const krb5_data *anonymous; trans.length = trans_in->length; trans.data = (char *) trans_in->data; @@ -327,6 +328,11 @@ krb5_check_transited_list (krb5_context ctx, const krb5_data *trans_in, (int) srealm->length, srealm->data)); if (trans.length == 0) return 0; + anonymous = krb5_anonymous_realm(); + if (crealm->length == anonymous->length + && (memcmp(crealm->data, anonymous->data, anonymous->length) == 0)) + return 0; /*Nothing to check for anonymous*/ + r = krb5_walk_realm_tree (ctx, crealm, srealm, &cdata.tgs, KRB5_REALM_BRANCH_CHAR); if (r) { diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c index 06b3c3874..315bdc943 100644 --- a/src/lib/krb5/krb/get_in_tkt.c +++ b/src/lib/krb5/krb/get_in_tkt.c @@ -283,6 +283,71 @@ cleanup: return (retval); } +/** + * Fully anonymous replies include a pa_pkinit_kx padata type including the KDC + * contribution key. This routine confirms that the session key is of the + * right form for fully anonymous requests. It is here rather than in the + * preauth code because the session key cannot be verified until the AS reply + * is decrypted and the preauth code all runs before the AS reply is decrypted. + */ +static krb5_error_code +verify_anonymous( krb5_context context, krb5_kdc_req *request, + krb5_kdc_rep *reply, krb5_keyblock *as_key) +{ + krb5_error_code ret = 0; + krb5_pa_data *pa; + krb5_data scratch; + krb5_keyblock *kdc_key = NULL, *expected = NULL; + krb5_enc_data *enc = NULL; + krb5_keyblock *session = reply->enc_part2->session; + if (!krb5_principal_compare_any_realm(context, request->client, + krb5_anonymous_principal())) + return 0; /*Only applies to fully anonymous*/ + pa = krb5int_find_pa_data(context, reply->padata, KRB5_PADATA_PKINIT_KX); + if (pa == NULL) + goto verification_error; + scratch.length = pa->length; + scratch.data = (char *) pa->contents; + ret = decode_krb5_enc_data( &scratch, &enc); + if (ret) + goto cleanup; + scratch.data = k5alloc(enc->ciphertext.length, &ret); + if (ret) + goto cleanup; + scratch.length = enc->ciphertext.length; + ret = krb5_c_decrypt(context, as_key, KRB5_KEYUSAGE_PA_PKINIT_KX, + NULL /*cipherstate*/, enc, &scratch); + if (ret) { + free( scratch.data); + goto cleanup; + } + ret = decode_krb5_encryption_key( &scratch, &kdc_key); + zap(scratch.data, scratch.length); + free(scratch.data); + if (ret) + goto cleanup; + ret = krb5_c_fx_cf2_simple( context, kdc_key, "PKINIT", + as_key, "KEYEXCHANGE", &expected); + if (ret) + goto cleanup; + if ((expected->enctype != session->enctype) + || (expected->length != session->length) + || (memcmp(expected->contents, session->contents, expected->length) != 0)) + goto verification_error; +cleanup: + if (kdc_key) + krb5_free_keyblock(context, kdc_key); + if (expected) + krb5_free_keyblock(context, expected); + if (enc) + krb5_free_enc_data(context, enc); + return ret; +verification_error: + ret = KRB5_KDCREP_MODIFIED; + krb5_set_error_message(context, ret, "Reply has wrong form of session key for anonymous request"); + goto cleanup; +} + static krb5_error_code verify_as_reply(krb5_context context, krb5_timestamp time_now, @@ -304,10 +369,14 @@ verify_as_reply(krb5_context context, * principal) and we requested (and received) a TGT. */ canon_req = ((request->kdc_options & KDC_OPT_CANONICALIZE) != 0) || - (krb5_princ_type(context, request->client) == KRB5_NT_ENTERPRISE_PRINCIPAL); + (krb5_princ_type(context, request->client) == KRB5_NT_ENTERPRISE_PRINCIPAL) + || (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS); if (canon_req) { canon_ok = IS_TGS_PRINC(context, request->server) && IS_TGS_PRINC(context, as_reply->enc_part2->server); + if ((!canon_ok ) && (request->kdc_options &KDC_OPT_REQUEST_ANONYMOUS)) + canon_ok = krb5_principal_compare_any_realm(context, as_reply->client, + krb5_anonymous_principal()); } else canon_ok = 0; @@ -1394,6 +1463,32 @@ krb5_init_creds_init(krb5_context context, ctx->salt.data = NULL; } + /*Anonymous*/ + if(opte->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) { + ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS; + /*Remap @REALM to WELLKNOWN/ANONYMOUS@REALM*/ + if (client->length == 1 && client->data[0].length ==0) { + krb5_principal new_client; + code = krb5_build_principal_ext(context, &new_client, client->realm.length, + client->realm.data, + strlen(KRB5_WELLKNOWN_NAMESTR), + KRB5_WELLKNOWN_NAMESTR, + strlen(KRB5_ANONYMOUS_PRINCSTR), + KRB5_ANONYMOUS_PRINCSTR, + 0); + if (code) + goto cleanup; + krb5_free_principal(context, ctx->request->client); + ctx->request->client = new_client; + krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN; + } + } + /*We will also handle anonymous if the input principal is the anonymous principal*/ + if (krb5_principal_compare_any_realm(context, ctx->request->client, + krb5_anonymous_principal())) { + ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS; + krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN; + } code = restart_init_creds_loop(context, ctx, NULL); *pctx = ctx; @@ -1829,6 +1924,10 @@ init_creds_step_reply(krb5_context context, ctx->request, ctx->reply); if (code != 0) goto cleanup; + code = verify_anonymous( context, ctx->request, ctx->reply, + &encrypting_key); + if (code) + goto cleanup; code = stash_as_reply(context, ctx->request_time, ctx->request, ctx->reply, &ctx->cred, NULL); diff --git a/src/lib/krb5/krb/gic_opt.c b/src/lib/krb5/krb/gic_opt.c index d326ac570..c94ee3487 100644 --- a/src/lib/krb5/krb/gic_opt.c +++ b/src/lib/krb5/krb/gic_opt.c @@ -53,6 +53,15 @@ krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opt, int canon } void KRB5_CALLCONV +krb5_get_init_creds_opt_set_anonymous (krb5_get_init_creds_opt *opt, + int anonymous) +{ + if (anonymous) + opt->flags |= KRB5_GET_INIT_CREDS_OPT_ANONYMOUS; + else opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_ANONYMOUS; +} + +void KRB5_CALLCONV krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt, krb5_enctype *etype_list, int etype_list_length) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST; @@ -149,8 +158,6 @@ krb5int_gic_opte_private_free(krb5_context context, krb5_gic_opt_ext *opte) free_gic_opt_ext_preauth_data(context, opte); if (opte->opt_private->fast_ccache_name) free(opte->opt_private->fast_ccache_name); - if (opte->opt_private->out_ccache) - krb5_cc_close(context, opte->opt_private->out_ccache); free(opte->opt_private); opte->opt_private = NULL; return 0; @@ -504,13 +511,8 @@ krb5_get_init_creds_opt_set_out_ccache(krb5_context context, "krb5_get_init_creds_opt_set_out_ccache"); if (retval) return retval; - if (opte->opt_private->out_ccache) { - krb5_cc_close(context, opte->opt_private->out_ccache); - opte->opt_private->out_ccache = NULL; - } - retval = krb5_cc_resolve(context, krb5_cc_get_name(context, ccache), - &opte->opt_private->out_ccache); - return retval; + opte->opt_private->out_ccache = ccache; + return 0; } krb5_error_code KRB5_CALLCONV diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index 1c35c4592..e7c191b63 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -109,6 +109,8 @@ krb5_address_compare krb5_address_order krb5_address_search krb5_aname_to_localname +krb5_anonymous_principal +krb5_anonymous_realm krb5_appdefault_boolean krb5_appdefault_string krb5_auth_con_free @@ -337,6 +339,7 @@ krb5_get_init_creds_opt_get_fast_flags krb5_get_init_creds_opt_get_pa krb5_get_init_creds_opt_init krb5_get_init_creds_opt_set_address_list +krb5_get_init_creds_opt_set_anonymous krb5_get_init_creds_opt_set_canonicalize krb5_get_init_creds_opt_set_change_password_prompt krb5_get_init_creds_opt_set_etype_list |
