From: Tom Yu Date: Sat, 29 Sep 2007 00:02:43 +0000 (+0000) Subject: pull up r19897 from trunk X-Git-Tag: krb5-1.6.3-beta2~56 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=7184197b4feeecbd3b9eccb4095d7e5a1a385c6e;p=krb5.git pull up r19897 from trunk r19897@cathode-dark-space: jaltman | 2007-08-29 18:38:26 -0400 ticket: new subject: NIM file ccache support improvements component: windows NIM supports the ability of the user to specify an explicit ccache name for use with an identity. If this ccache is a FILE ccache, we need to be able to store credentials into the ccache. krb5cred.dll did not previously specify the KRB5_TC_OPENCLOSE flag on the ccache when setting other flags such as KRB5_TC_NOTICKET (which is used with MSLSA ccaches). As a result, open/close mode was turned off, the ccache file would be opened in read-only mode and attempts to store credentials into the ccache would fail. This is fixed by specifying KRB5_TC_OPENCLOSE when setting the ccache flags. When a CCAPI implementation is unavailable, we need to automatically generate the FILE ccache name if one has not already been specified. We default to a file stored in the user's Local Settings\Temp directory. The generated ccache is then added to the file ccache watch list. Finally, some users have complained about the behavior of Microsoft Vista's UAC mode and how it makes the CCAPI cache useless for storing credentials that must be used in conjunction with processes that do not have restricted privileges since those processes run in a separate logon session. For these users we have added a "DefaultToFileCache" registry value that can be specified to force the use of FILE ccaches in preference to CCAPI ccaches when there is no explicit ccache specified for a given identity. Unlike CCAPI ccaches, the FILE ccaches are accessible from both restricted and unrestricted processes when UAC is active. ticket: 5703 version_fixed: 1.6.3 git-svn-id: svn://anonsvn.mit.edu/krb5/branches/krb5-1-6@20009 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/windows/identity/plugins/krb5/krb5configid.c b/src/windows/identity/plugins/krb5/krb5configid.c index a2688e1e7..2f3fe62c8 100644 --- a/src/windows/identity/plugins/krb5/krb5configid.c +++ b/src/windows/identity/plugins/krb5/krb5configid.c @@ -61,8 +61,7 @@ k5_id_read_params(k5_id_dlg_data * d) { khm_size cb; khm_int32 rv; khm_int32 t; - khm_handle csp_ident; - khm_handle csp_idroot = NULL; + khm_handle csp_ident = NULL; cb = sizeof(idname); rv = khui_cfg_get_name(d->cfg.ctx_node, idname, &cb); @@ -75,17 +74,7 @@ k5_id_read_params(k5_id_dlg_data * d) { assert(KHM_SUCCEEDED(rv)); #endif - rv = kcdb_identity_get_config(d->ident, 0, &csp_idroot); - if (KHM_SUCCEEDED(rv) && - KHM_SUCCEEDED(khc_open_space(csp_idroot, CSNAME_KRB5CRED, 0, - &csp_ident))) { - khc_shadow_space(csp_ident, csp_params); - } else { - csp_ident = csp_params; - } - - if (csp_idroot) - khc_close_space(csp_idroot); + khm_krb5_get_identity_config(d->ident, 0, &csp_ident); rv = khc_read_int32(csp_ident, L"DefaultLifetime", &t); if (KHM_SUCCEEDED(rv)) @@ -146,7 +135,7 @@ k5_id_read_params(k5_id_dlg_data * d) { d->tc_renew.min = 0; d->tc_renew.max = 3600 * 24 * 30; - if (csp_ident != csp_params) + if (csp_ident) khc_close_space(csp_ident); } diff --git a/src/windows/identity/plugins/krb5/krb5funcs.c b/src/windows/identity/plugins/krb5/krb5funcs.c index 4dc854845..9c8db7509 100644 --- a/src/windows/identity/plugins/krb5/krb5funcs.c +++ b/src/windows/identity/plugins/krb5/krb5funcs.c @@ -515,8 +515,6 @@ static long get_tickets_from_cache(krb5_context ctx, #ifdef KRB5_TC_NOTICKET flags = KRB5_TC_NOTICKET; -#else - flags = 0; #endif { @@ -1005,6 +1003,7 @@ khm_krb5_renew_cred(khm_handle cred) khm_boolean istgt = FALSE; khm_int32 flags; + int ccflags = 0; cbname = sizeof(wname); kcdb_cred_get_name(cred, wname, &cbname); @@ -1055,9 +1054,8 @@ khm_krb5_renew_cred(khm_handle cred) in_creds.client = me; in_creds.server = server; -#ifdef KRB5_TC_NOTICKET - pkrb5_cc_set_flags(ctx, cc, 0); -#endif + ccflags = KRB5_TC_OPENCLOSE; + pkrb5_cc_set_flags(ctx, cc, ccflags); if (strlen("krbtgt") != krb5_princ_name(ctx, server)->length || strncmp("krbtgt", krb5_princ_name(ctx, server)->data, krb5_princ_name(ctx, server)->length)) @@ -1080,9 +1078,6 @@ khm_krb5_renew_cred(khm_handle cred) code = pkrb5_get_renewed_creds(ctx, &cc_creds, me, cc, NULL); } -#ifdef KRB5_TC_NOTICKET - pkrb5_cc_set_flags(ctx, cc, KRB5_TC_NOTICKET); -#endif if (code) { if ( code != KRB5KDC_ERR_ETYPE_NOSUPP || code != KRB5_KDC_UNREACH) @@ -1094,6 +1089,8 @@ khm_krb5_renew_cred(khm_handle cred) code = pkrb5_cc_initialize(ctx, cc, me); if (code) goto cleanup; + ccflags = KRB5_TC_OPENCLOSE; + pkrb5_cc_set_flags(ctx, cc, ccflags); } code = pkrb5_cc_store_cred(ctx, cc, istgt ? &cc_creds : out_creds); @@ -1101,7 +1098,6 @@ khm_krb5_renew_cred(khm_handle cred) cleanup: - if (in_creds.client == me) in_creds.client = NULL; if (in_creds.server == server) @@ -1147,6 +1143,7 @@ khm_krb5_renew_ident(khm_handle identity) wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; khm_size cb; khm_int32 k5_flags; + int ccflags; memset(&my_creds, 0, sizeof(krb5_creds)); @@ -1304,13 +1301,13 @@ khm_krb5_renew_ident(khm_handle identity) my_creds.client = me; my_creds.server = server; -#ifdef KRB5_TC_NOTICKET - pkrb5_cc_set_flags(ctx, cc, 0); -#endif + pkrb5_cc_set_flags(ctx, cc, KRB5_TC_OPENCLOSE); code = pkrb5_get_renewed_creds(ctx, &my_creds, me, cc, NULL); + ccflags = KRB5_TC_OPENCLOSE; #ifdef KRB5_TC_NOTICKET - pkrb5_cc_set_flags(ctx, cc, KRB5_TC_NOTICKET); + ccflags |= KRB5_TC_NOTICKET; #endif + pkrb5_cc_set_flags(ctx, cc, ccflags); if (code) { if ( code != KRB5KDC_ERR_ETYPE_NOSUPP || code != KRB5_KDC_UNREACH) @@ -1321,6 +1318,9 @@ khm_krb5_renew_ident(khm_handle identity) code = pkrb5_cc_initialize(ctx, cc, me); if (code) goto cleanup; + code = pkrb5_cc_set_flags(ctx, cc, KRB5_TC_OPENCLOSE); + if (code) goto cleanup; + code = pkrb5_cc_store_cred(ctx, cc, &my_creds); if (code) goto cleanup; @@ -3057,31 +3057,130 @@ get_libdefault_string(profile_t profile, const char * realm, return code; } + +const struct escape_char_sequences { + wchar_t character; + wchar_t escape; +} file_cc_escapes[] = { + + /* in ASCII order */ + + {L'\"', L'd'}, + {L'$', L'$'}, + {L'%', L'r'}, + {L'\'', L'i'}, + {L'*', L's'}, + {L'/', L'f'}, + {L':', L'c'}, + {L'<', L'l'}, + {L'>', L'g'}, + {L'?', L'q'}, + {L'\\', L'b'}, + {L'|', L'p'} +}; + +static void +escape_string_for_filename(const wchar_t * s, + wchar_t * buf, + khm_size cb_buf) +{ + wchar_t * d; + int i; + + for (d = buf; *s && cb_buf > sizeof(wchar_t) * 3; s++) { + if (iswpunct(*s)) { + for (i=0; i < ARRAYLENGTH(file_cc_escapes); i++) { + if (*s == file_cc_escapes[i].character) + break; + } + + if (i < ARRAYLENGTH(file_cc_escapes)) { + *d++ = L'$'; + *d++ = file_cc_escapes[i].escape; + cb_buf -= sizeof(wchar_t) * 2; + continue; + } + } + + *d++ = *s; + cb_buf -= sizeof(wchar_t); + } + +#ifdef DEBUG + assert(cb_buf >= sizeof(wchar_t)); +#endif + *d++ = L'\0'; +} + +static khm_int32 +get_default_file_cache_for_identity(const wchar_t * idname, + wchar_t * ccname, + khm_size * pcb) +{ + wchar_t escf[MAX_PATH] = L""; + wchar_t tmppath[MAX_PATH] = L""; + wchar_t tccname[MAX_PATH]; + khm_size cb; + + escape_string_for_filename(idname, escf, sizeof(escf)); + GetTempPath(ARRAYLENGTH(tmppath), tmppath); + + StringCbPrintf(tccname, sizeof(tccname), L"FILE:%s\\krb5cc.%s", tmppath, escf); + StringCbLength(tccname, sizeof(tccname), &cb); + cb += sizeof(wchar_t); + + if (ccname && *pcb >= cb) { + StringCbCopy(ccname, *pcb, tccname); + *pcb = cb; + return KHM_ERROR_SUCCESS; + } else { + *pcb = cb; + return KHM_ERROR_TOO_LONG; + } +} + khm_int32 khm_krb5_get_identity_default_ccache(khm_handle ident, wchar_t * buf, khm_size * pcb) { khm_handle csp_id = NULL; khm_int32 rv = KHM_ERROR_SUCCESS; + khm_size cbt; rv = khm_krb5_get_identity_config(ident, 0, &csp_id); + cbt = *pcb; if (KHM_SUCCEEDED(rv)) - rv = khc_read_string(csp_id, L"DefaultCCName", buf, pcb); + rv = khc_read_string(csp_id, L"DefaultCCName", buf, &cbt); - if (KHM_FAILED(rv) && rv != KHM_ERROR_TOO_LONG) { + if ((KHM_FAILED(rv) && rv != KHM_ERROR_TOO_LONG) || + (KHM_SUCCEEDED(rv) && buf[0] == L'\0')) { /* we need to figure out the default ccache from the principal name */ wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; wchar_t ccname[MAX_PATH]; khm_size cb; + khm_int32 use_file_cache = 0; + + khc_read_int32(csp_id, L"DefaultToFileCache", &use_file_cache); cb = sizeof(idname); kcdb_identity_get_name(ident, idname, &cb); - StringCbCopy(ccname, sizeof(ccname), idname); + + if (use_file_cache) { + cb = sizeof(ccname); + rv = get_default_file_cache_for_identity(idname, ccname, &cb); +#ifdef DEBUG + assert(KHM_SUCCEEDED(rv)); +#endif + } else { /* generate an API: cache */ + StringCbPrintf(ccname, sizeof(ccname), L"API:%s", idname); + } khm_krb5_canon_cc_name(ccname, sizeof(ccname)); - StringCbLength(ccname, sizeof(ccname), &cb); _reportf(L"Setting CCache [%s] for identity [%s]", ccname, idname); + StringCbLength(ccname, sizeof(ccname), &cb); + cb += sizeof(wchar_t); + if (buf && *pcb >= cb) { StringCbCopy(buf, *pcb, ccname); *pcb = cb; @@ -3094,6 +3193,8 @@ khm_krb5_get_identity_default_ccache(khm_handle ident, wchar_t * buf, khm_size * wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; khm_size cb; + *pcb = cbt; + cb = sizeof(idname); kcdb_identity_get_name(ident, idname, &cb); diff --git a/src/windows/identity/plugins/krb5/krb5newcreds.c b/src/windows/identity/plugins/krb5/krb5newcreds.c index 67fb8dac0..410393caf 100644 --- a/src/windows/identity/plugins/krb5/krb5newcreds.c +++ b/src/windows/identity/plugins/krb5/krb5newcreds.c @@ -181,7 +181,7 @@ k5_handle_wmnc_notify(HWND hwnd, case WMNC_DIALOG_MOVE: { k5_dlg_data * d; - + d = (k5_dlg_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); @@ -1355,34 +1355,9 @@ k5_read_dlg_params(k5_dlg_data * d, khm_handle identity) d->sync = FALSE; } -void -k5_write_dlg_params(k5_dlg_data * d, khm_handle identity, char * ccache) +void +k5_ensure_identity_ccache_is_watched(khm_handle identity, char * ccache) { - - k5_params p; - - ZeroMemory(&p, sizeof(p)); - - p.source_reg = K5PARAM_FM_ALL; /* we want to write all the - settings to the registry, if - necessary. */ - - p.renewable = d->renewable; - p.forwardable = d->forwardable; - p.proxiable = d->proxiable; - p.addressless = d->addressless; - p.publicIP = d->publicIP; - - p.lifetime = (krb5_deltat) d->tc_lifetime.current; - p.lifetime_max = (krb5_deltat) d->tc_lifetime.max; - p.lifetime_min = (krb5_deltat) d->tc_lifetime.min; - - p.renew_life = (krb5_deltat) d->tc_renew.current; - p.renew_life_max = (krb5_deltat) d->tc_renew.max; - p.renew_life_min = (krb5_deltat) d->tc_renew.min; - - khm_krb5_set_identity_params(identity, &p); - /* if we used a FILE: ccache, we should add it to FileCCList. Otherwise the tickets are not going to get listed. */ do { @@ -1462,6 +1437,37 @@ k5_write_dlg_params(k5_dlg_data * d, khm_handle identity, char * ccache) PFREE(mlist); } while(FALSE); +} + +void +k5_write_dlg_params(k5_dlg_data * d, khm_handle identity, char * ccache) +{ + + k5_params p; + + ZeroMemory(&p, sizeof(p)); + + p.source_reg = K5PARAM_FM_ALL; /* we want to write all the + settings to the registry, if + necessary. */ + + p.renewable = d->renewable; + p.forwardable = d->forwardable; + p.proxiable = d->proxiable; + p.addressless = d->addressless; + p.publicIP = d->publicIP; + + p.lifetime = (krb5_deltat) d->tc_lifetime.current; + p.lifetime_max = (krb5_deltat) d->tc_lifetime.max; + p.lifetime_min = (krb5_deltat) d->tc_lifetime.min; + + p.renew_life = (krb5_deltat) d->tc_renew.current; + p.renew_life_max = (krb5_deltat) d->tc_renew.max; + p.renew_life_min = (krb5_deltat) d->tc_renew.min; + + khm_krb5_set_identity_params(identity, &p); + + k5_ensure_identity_ccache_is_watched(identity, ccache); /* as in k5_read_dlg_params, once we write the data in, the local data is no longer dirty */ @@ -2059,6 +2065,9 @@ k5_msg_cred_dialog(khm_int32 msg_type, if(g_fjob.state == FIBER_STATE_NONE) { wchar_t msg[KHUI_MAXCCH_BANNER]; khm_size cb; + int code; + + code = g_fjob.code; /* Special case. If the users' password has expired, we force a password change dialog on @@ -2143,7 +2152,7 @@ k5_msg_cred_dialog(khm_int32 msg_type, k5_free_kinit_job(); if (is_k5_identpro) { - if (g_fjob.code == 0) + if (code == 0) kcdb_identity_set_flags(ident, KCDB_IDENT_FLAG_VALID, KCDB_IDENT_FLAG_VALID); @@ -2832,6 +2841,9 @@ k5_msg_cred_dialog(khm_int32 msg_type, imported = khm_krb5_ms2mit(NULL, (t == K5_LSAIMPORT_MATCH), TRUE, &id_imported); if (imported) { + if (id_imported) + k5_ensure_identity_ccache_is_watched(id_imported, NULL); + khm_krb5_list_tickets(&ctx); if (ctx) diff --git a/src/windows/identity/plugins/krb5/krbconfig.csv b/src/windows/identity/plugins/krb5/krbconfig.csv index 4dc5b7d72..cb4a860d9 100644 --- a/src/windows/identity/plugins/krb5/krbconfig.csv +++ b/src/windows/identity/plugins/krb5/krbconfig.csv @@ -27,6 +27,7 @@ Krb5Cred,KC_SPACE,0,Kerberos V Credentials Provider LastDefaultIdent,KC_STRING,,Last known default identity PromptCacheLifetime,KC_INT32,172800,Lifetime of the prompt cache in seconds DefaultCCName,KC_STRING,,Default CC name (only per identity) + DefaultToFileCache,KC_INT32,0,"If no DefaultCCName is specified for an identity, use a generated FILE: cache instead of an API: cache" PromptCache,KC_SPACE,0,Cache of prompts (only per identity) Name,KC_STRING,, Banner,KC_STRING,,