static void check_and_set_refresh_bit_for_identity(khm_handle cred, khm_handle * plast_identity) { khm_handle this_identity; if (KHM_SUCCEEDED(kcdb_cred_get_identity(cred, &this_identity))) { if (!kcdb_identity_is_equal(this_identity, *plast_identity)) { kcdb_identity_set_flags(this_identity, KCDB_IDENT_FLAG_NEEDREFRESH, KCDB_IDENT_FLAG_NEEDREFRESH); kcdb_identity_hold(this_identity); if (*plast_identity) kcdb_identity_release(*plast_identity); *plast_identity = this_identity; } kcdb_identity_release(this_identity); this_identity = NULL; } }
static void write_params_ident(ident_data * d) { khm_handle csp_ident; if (d->saved.monitor == d->work.monitor && d->saved.auto_renew == d->work.auto_renew && d->saved.sticky == d->work.sticky && !d->removed) return; if (KHM_FAILED(kcdb_identity_get_config(d->ident, KHM_PERM_WRITE, &csp_ident))) { #ifdef DEBUG assert(FALSE); #endif return; } if (d->removed) { khm_handle h = NULL; khm_int32 flags = 0; khc_remove_space(csp_ident); /* calling kcdb_identity_get_config() will update the KCDB_IDENT_FLAG_CONFIG flag for the identity to reflect the fact that it nolonger has a configuration. */ kcdb_identity_get_config(d->ident, 0, &h); if (h) { /* what the ? */ #ifdef DEBUG assert(FALSE); #endif khc_close_space(h); } #ifdef DEBUG kcdb_identity_get_flags(d->ident, &flags); assert(!(flags & KCDB_IDENT_FLAG_CONFIG)); #endif } else { if (d->saved.monitor != d->work.monitor) khc_write_int32(csp_ident, L"Monitor", !!d->work.monitor); if (d->saved.auto_renew != d->work.auto_renew) khc_write_int32(csp_ident, L"AllowAutoRenew", !!d->work.auto_renew); if (d->saved.sticky != d->work.sticky) { kcdb_identity_set_flags(d->ident, (d->work.sticky)?KCDB_IDENT_FLAG_STICKY:0, KCDB_IDENT_FLAG_STICKY); } } khc_close_space(csp_ident); d->saved = d->work; d->applied = TRUE; if (d->hwnd) PostMessage(d->hwnd, KHUI_WM_CFG_NOTIFY, MAKEWPARAM(0, WMCFG_UPDATE_STATE), 0); khm_refresh_config(); }
/* Executed inside a task object */ static khm_int32 __stdcall kinit_task_proc(void * vparam) { k5_kinit_task * kt; long l; kt = (k5_kinit_task *) vparam; if (kt == NULL || kt->magic != K5_KINIT_TASK_MAGIC) { assert(FALSE); return KHM_ERROR_INVALID_PARAM; } EnterCriticalSection(&kt->cs); if (kt->state == K5_KINIT_STATE_ABORTED) { LeaveCriticalSection(&kt->cs); return KHM_ERROR_SUCCESS; } if (kt->state != K5_KINIT_STATE_PREP) { LeaveCriticalSection(&kt->cs); assert(FALSE); return KHM_ERROR_INVALID_OPERATION; } kt->prompt_set_index = 0; if (cached_kinit_prompter(kt)) { kt->state = K5_KINIT_STATE_WAIT; kcdb_identity_set_flags(kt->identity, KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_KEY_EXPORT, KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_KEY_EXPORT); khui_cw_notify_identity_state(kt->nc, kt->nct->hwnd_panel, L"", KHUI_CWNIS_VALIDATED | KHUI_CWNIS_READY, 0); SetEvent(kt->h_parent_wait); LeaveCriticalSection(&kt->cs); WaitForSingleObject(kt->h_task_wait, INFINITE); EnterCriticalSection(&kt->cs); assert(kt->state == K5_KINIT_STATE_CONFIRM || kt->state == K5_KINIT_STATE_ABORTED); if (kt->state != K5_KINIT_STATE_CONFIRM) goto done; if (!cp_check_continue(kt)) { kt->kinit_code = KRB5KRB_AP_ERR_BAD_INTEGRITY; goto done; } } else { kt->state = K5_KINIT_STATE_INCALL; } call_kinit: #ifdef DEBUG _reportf(L"kinit task state prior to calling khm_krb5_kinit() :"); _reportf(L" kt->principal = [%S]", kt->principal); _reportf(L" kt->kinit_code = %d", kt->kinit_code); _reportf(L" kt->state = %d", kt->state); _reportf(L" kt->prompt_set_index = %d", kt->prompt_set_index); _reportf(L" kt->is_valid_principal = %d", (int) kt->is_valid_principal); _reportf(L" kt->ccache = [%s]", kt->ccache); #endif LeaveCriticalSection(&kt->cs); if (kt->context == 0) krb5_init_context(&kt->context); l = khm_krb5_kinit (kt->context, kt->principal, kt->password, kt->ccache, kt->params.lifetime, (kt->is_valid_principal) ? kt->params.forwardable : 0, (kt->is_valid_principal) ? kt->params.proxiable : 0, (kt->is_valid_principal && kt->params.renewable) ? kt->params.renew_life : 0, kt->params.addressless, kt->params.publicIP, kt->params.allow_weak_crypto, kinit_prompter, kt); EnterCriticalSection(&kt->cs); kt->kinit_code = l; _reportf(L" kinit return code = %d", (int) l); if (kt->state == K5_KINIT_STATE_RETRY) { /* If the principal was found to be valid, and if we restricted the options that were being passed to kinit, then we need to retry the kinit call. This time we use the real options. */ assert(kt->is_valid_principal); kt->state = K5_KINIT_STATE_INCALL; goto call_kinit; } assert(kt->state == K5_KINIT_STATE_INCALL || kt->state == K5_KINIT_STATE_CONFIRM || kt->state == K5_KINIT_STATE_ABORTED); if (kt->state == K5_KINIT_STATE_ABORTED) { goto done; } if (kt->state == K5_KINIT_STATE_CONFIRM) { goto done; } /* The call completed without any prompting. We haven't had a chance to update the identity state. */ if (kt->state == K5_KINIT_STATE_INCALL) { wchar_t msg[KHUI_MAXCCH_BANNER]; switch (kt->kinit_code) { case 0: kcdb_identity_set_flags(kt->identity, KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_KEY_EXPORT, KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_KEY_EXPORT); khui_cw_notify_identity_state(kt->nc, kt->nct->hwnd_panel, L"", KHUI_CWNIS_READY | KHUI_CWNIS_VALIDATED, 0); khui_cw_clear_prompts(kt->nc); break; case KRB5KDC_ERR_KEY_EXP: kcdb_identity_set_flags(kt->identity, KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_KEY_EXPORT, KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_KEY_EXPORT); k5_force_password_change(kt->dlg_data); LoadString(hResModule, IDS_K5ERR_KEY_EXPIRED, msg, ARRAYLENGTH(msg)); khui_cw_notify_identity_state(kt->nc, kt->nct->hwnd_panel, msg, KHUI_CWNIS_READY | KHUI_CWNIS_VALIDATED, 0); break; case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: kcdb_identity_set_flags(kt->identity, KCDB_IDENT_FLAG_INVALID, KCDB_IDENT_FLAG_INVALID); khui_cw_clear_prompts(kt->nc); khm_err_describe(kt->context, kt->kinit_code, msg, sizeof(msg), NULL, NULL); khui_cw_notify_identity_state(kt->nc, kt->nct->hwnd_panel, msg, KHUI_CWNIS_VALIDATED, 0); break; default: kcdb_identity_set_flags(kt->identity, KCDB_IDENT_FLAG_UNKNOWN, KCDB_IDENT_FLAG_UNKNOWN); khui_cw_clear_prompts(kt->nc); khm_err_describe(kt->context, kt->kinit_code, msg, sizeof(msg), NULL, NULL); khui_cw_notify_identity_state(kt->nc, kt->nct->hwnd_panel, msg, KHUI_CWNIS_VALIDATED, 0); } } done: kt->state = K5_KINIT_STATE_DONE; LeaveCriticalSection(&kt->cs); SetEvent(kt->h_parent_wait); k5_kinit_task_release(kt); return KHM_ERROR_SUCCESS; }
static krb5_error_code KRB5_CALLCONV kinit_prompter(krb5_context context, void *data, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[]) { int i; k5_kinit_task * kt; khm_size ncp; krb5_error_code code = 0; BOOL new_prompts = TRUE; khm_handle csp_prcache = NULL; kt = (k5_kinit_task *) data; assert(kt && kt->magic == K5_KINIT_TASK_MAGIC); EnterCriticalSection(&kt->cs); if (kt->state == K5_KINIT_STATE_ABORTED) { LeaveCriticalSection(&kt->cs); return KRB5_LIBOS_PWDINTR; } #ifdef DEBUG assert(kt->state == K5_KINIT_STATE_INCALL || kt->state == K5_KINIT_STATE_CONFIRM); _reportf(L"k5_kinit_prompter() received %d prompts with name=[%S] banner=[%S]", num_prompts, name, banner); for (i=0; i < num_prompts; i++) { _reportf(L"Prompt[%d]: string[%S]", i, prompts[i].prompt); } #endif /* we got prompts? Then we assume that the principal is valid */ if (!kt->is_valid_principal) { kt->is_valid_principal = TRUE; /* if the flags that were used to call kinit were restricted because we didn't know the validity of the principal, then we need to go back and retry the call with the correct flags. */ if (kt->params.forwardable || kt->params.proxiable || kt->params.renewable) { _reportf(L"Retrying kinit call due to restricted flags on first call."); kt->state = K5_KINIT_STATE_RETRY; LeaveCriticalSection(&kt->cs); return KRB5_LIBOS_PWDINTR; } } /* check if we are already showing the right prompts */ khui_cw_get_prompt_count(kt->nc, &ncp); if (num_prompts != (int) ncp && num_prompts != 0) goto _show_new_prompts; for (i=0; i < num_prompts; i++) { wchar_t wprompt[KHUI_MAXCCH_PROMPT]; khui_new_creds_prompt * p; if(prompts[i].prompt) { AnsiStrToUnicode(wprompt, sizeof(wprompt), prompts[i].prompt); } else { wprompt[0] = L'\0'; } if (KHM_FAILED(khui_cw_get_prompt(kt->nc, i, &p))) break; if ( /* if we received a prompt string, then it should be the same as the one that is displayed */ (wprompt[0] != L'\0' && (p->prompt == NULL || wcscmp(wprompt, p->prompt))) || /* if we didn't receive one, then there shouldn't be one displayed. This case really shouldn't happen in reality, but we check anyway. */ (wprompt[0] == L'\0' && p->prompt != NULL) || /* the type should match */ (prompts[i].type != p->type) || /* if this prompt should be hidden, then it must also be so */ (prompts[i].hidden && !(p->flags & KHUI_NCPROMPT_FLAG_HIDDEN)) || (!prompts[i].hidden && (p->flags & KHUI_NCPROMPT_FLAG_HIDDEN)) ) break; } if (i >= num_prompts) { new_prompts = FALSE; /* ok. looks like we are already showing the same set of prompts that we were supposed to show. Sync up the values and go ahead. */ goto _process_prompts; } _show_new_prompts: if (num_prompts == 0) { assert(FALSE); khui_cw_notify_identity_state(kt->nc, kt->nct->hwnd_panel, NULL, KHUI_CWNIS_READY | KHUI_CWNIS_NOPROGRESS | KHUI_CWNIS_VALIDATED, 0); code = 0; kt->is_null_password = TRUE; goto _process_prompts; } /* in addition to showing new prompts, we also cache the first set of prompts. */ if (kt->prompt_set_index == 0) { khm_handle csp_idconfig = NULL; khm_handle csp_idk5 = NULL; kcdb_identity_get_config(kt->identity, KHM_FLAG_CREATE, &csp_idconfig); if (csp_idconfig != NULL) khc_open_space(csp_idconfig, CSNAME_KRB5CRED, KHM_FLAG_CREATE, &csp_idk5); if (csp_idk5 != NULL) khc_open_space(csp_idk5, CSNAME_PROMPTCACHE, KHM_FLAG_CREATE, &csp_prcache); khc_close_space(csp_idconfig); khc_close_space(csp_idk5); } { wchar_t wbanner[KHUI_MAXCCH_BANNER]; wchar_t wname[KHUI_MAXCCH_PNAME]; if(banner) AnsiStrToUnicode(wbanner, sizeof(wbanner), banner); else wbanner[0] = L'\0'; if(name) AnsiStrToUnicode(wname, sizeof(wname), name); else LoadString(hResModule, IDS_PNAME_PW, wname, ARRAYLENGTH(wname)); khui_cw_clear_prompts(kt->nc); khui_cw_begin_custom_prompts(kt->nc, num_prompts, wbanner, wname); if (csp_prcache) { FILETIME current; FILETIME lifetime; FILETIME expiry; khm_int64 iexpiry; khm_int32 t = 0; khc_write_string(csp_prcache, L"Banner", wbanner); khc_write_string(csp_prcache, L"Name", (name)? wname: L""); khc_write_int32(csp_prcache, L"PromptCount", (khm_int32) num_prompts); GetSystemTimeAsFileTime(¤t); #ifdef USE_PROMPT_CACHE_LIFETIME khc_read_int32(csp_params, L"PromptCacheLifetime", &t); if (t == 0) t = 172800; /* 48 hours */ #else khc_read_int32(csp_params, L"MaxRenewLifetime", &t); if (t == 0) t = 2592000; /* 30 days */ t += 604800; /* + 7 days */ #endif TimetToFileTimeInterval(t, &lifetime); expiry = FtAdd(¤t, &lifetime); iexpiry = FtToInt(&expiry); khc_write_int64(csp_prcache, L"ExpiresOn", iexpiry); } } for(i=0; i < num_prompts; i++) { wchar_t wprompt[KHUI_MAXCCH_PROMPT]; if(prompts[i].prompt) { AnsiStrToUnicode(wprompt, sizeof(wprompt), prompts[i].prompt); } else { wprompt[0] = 0; } khui_cw_add_prompt(kt->nc, prompts[i].type, wprompt, NULL, (prompts[i].hidden?KHUI_NCPROMPT_FLAG_HIDDEN:0)); if (csp_prcache) { khm_handle csp_p = NULL; wchar_t wnum[8]; /* should be enough for 10 million prompts */ wnum[0] = 0; StringCbPrintf(wnum, sizeof(wnum), L"%d", i); khc_open_space(csp_prcache, wnum, KHM_FLAG_CREATE, &csp_p); if (csp_p) { khc_write_string(csp_p, L"Prompt", wprompt); khc_write_int32(csp_p, L"Type", prompts[i].type); khc_write_int32(csp_p, L"Flags", (prompts[i].hidden? KHUI_NCPROMPT_FLAG_HIDDEN:0)); khc_close_space(csp_p); } } } if (csp_prcache) { khc_close_space(csp_prcache); csp_prcache = NULL; } _process_prompts: if (new_prompts) { kt->state = K5_KINIT_STATE_WAIT; kcdb_identity_set_flags(kt->identity, KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_KEY_EXPORT, KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_KEY_EXPORT); khui_cw_notify_identity_state(kt->nc, kt->nct->hwnd_panel, L"", KHUI_CWNIS_VALIDATED | KHUI_CWNIS_READY, 0); SetEvent(kt->h_parent_wait); LeaveCriticalSection(&kt->cs); WaitForSingleObject(kt->h_task_wait, INFINITE); EnterCriticalSection(&kt->cs); } /* we get here after the user selects an action that either cancels the credentials acquisition operation or triggers the actual acquisition of credentials. */ if (kt->state != K5_KINIT_STATE_INCALL && kt->state != K5_KINIT_STATE_CONFIRM) { code = KRB5_LIBOS_PWDINTR; goto _exit; } kt->is_null_password = FALSE; /* otherwise, we need to get the data back from the UI and return 0 */ khui_cw_sync_prompt_values(kt->nc); for(i=0; i<num_prompts; i++) { krb5_data * d; wchar_t wbuf[512]; khm_size cbbuf; size_t cch; d = prompts[i].reply; cbbuf = sizeof(wbuf); if(KHM_SUCCEEDED(khui_cw_get_prompt_value(kt->nc, i, wbuf, &cbbuf))) { UnicodeStrToAnsi(d->data, d->length, wbuf); if(SUCCEEDED(StringCchLengthA(d->data, d->length, &cch))) d->length = (unsigned int) cch; else d->length = 0; } else { assert(FALSE); d->length = 0; } if (prompts[i].type == KRB5_PROMPT_TYPE_PASSWORD && d->length == 0) kt->is_null_password = TRUE; } if (khui_cw_get_persist_private_data(kt->nc) && num_prompts == 1 && prompts[0].type == KRB5_PROMPT_TYPE_PASSWORD && prompts[0].reply->length != 0) { k5_reply_to_acqpriv_id_request(kt->nc, prompts[0].reply); } _exit: kt->prompt_set_index++; LeaveCriticalSection(&kt->cs); /* entering a NULL password is equivalent to cancelling out */ if (kt->is_null_password) return KRB5_LIBOS_PWDINTR; else return code; }