#include<krbcred.h>
#include<kherror.h>
#include<khmsgtypes.h>
#include<khuidefs.h>
#include<utils.h>
#include<commctrl.h>
#include<strsafe.h>
#include<krb5.h>
#include<assert.h>
#define K4_METHOD_AUTO 0
#define K4_METHOD_PASSWORD 1
#define K4_METHOD_K524 2
int method_to_id[] = {
IDC_NCK4_AUTO,
IDC_NCK4_PWD,
IDC_NCK4_K524
};
typedef struct tag_k4_dlg_data {
HWND hwnd;
khui_new_creds * nc;
khui_new_creds_by_type * nct;
khm_boolean k4_enabled;
khm_int32 method;
time_t lifetime;
} k4_dlg_data;
void k4_update_display(k4_dlg_data * d, BOOL update_methods) {
CheckDlgButton(d->hwnd, IDC_NCK4_OBTAIN,
(d->k4_enabled)?BST_CHECKED: BST_UNCHECKED);
if (d->k4_enabled) {
EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_AUTO), TRUE);
EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_K524), TRUE);
EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_PWD ), TRUE);
} else {
EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_AUTO), FALSE);
EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_K524), FALSE);
EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_PWD ), FALSE);
}
#ifdef DEBUG
assert(d->method >= 0 && d->method < ARRAYLENGTH(method_to_id));
#endif
CheckRadioButton(d->hwnd, IDC_NCK4_AUTO, IDC_NCK4_PWD, method_to_id[d->method]);
khui_cw_enable_type(d->nc, credtype_id_krb4, d->k4_enabled);
}
void k4_update_data(k4_dlg_data * d) {
int i;
khm_boolean oldstate;
oldstate = d->k4_enabled;
if (IsDlgButtonChecked(d->hwnd, IDC_NCK4_OBTAIN) == BST_CHECKED)
d->k4_enabled = TRUE;
else
d->k4_enabled = FALSE;
if ((oldstate && !d->k4_enabled) ||
(!oldstate && d->k4_enabled)) {
khui_cw_enable_type(d->nc, credtype_id_krb4, d->k4_enabled);
}
d->method = K4_METHOD_AUTO;
for (i=K4_METHOD_AUTO; i<=K4_METHOD_K524; i++) {
if (IsDlgButtonChecked(d->hwnd, method_to_id[i]) == BST_CHECKED) {
d->method = i;
break;
}
}
}
khm_boolean k4_should_identity_get_k4(khm_handle ident) {
khm_int32 idflags = 0;
khm_int32 t = TRUE;
khm_handle csp_ident = NULL;
khm_handle csp_k4 = NULL;
khm_boolean get_k4 = TRUE;
khm_boolean id_spec = FALSE;
if (KHM_FAILED(kcdb_identity_get_flags(ident, &idflags)))
return FALSE;
if (!(idflags & KCDB_IDENT_FLAG_DEFAULT)) {
khm_handle defident = NULL;
if (KHM_SUCCEEDED(kcdb_identity_get_default(&defident))) {
kcdb_identity_release(defident);
return FALSE;
}
}
if (KHM_SUCCEEDED(kcdb_identity_get_config(ident, 0, &csp_ident))) {
if (KHM_SUCCEEDED(khc_open_space(csp_ident, CSNAME_KRB4CRED, 0,
&csp_k4))) {
khm_int32 t = 0;
if (KHM_SUCCEEDED(khc_read_int32(csp_k4, L"Krb4NewCreds", &t))) {
get_k4 = !!t;
id_spec = TRUE;
}
khc_close_space(csp_k4);
}
khc_close_space(csp_ident);
}
if (id_spec || !get_k4)
return get_k4;
if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"Krb4NewCreds", &t)) &&
!t)
return FALSE;
return TRUE;
}
void k4_read_identity_data(k4_dlg_data * d) {
khm_handle csp_ident = NULL;
khm_handle csp_k4 = NULL;
khm_int32 idflags = 0;
khm_int32 t;
if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"Krb4NewCreds", &t)))
d->k4_enabled = !!t;
else
d->k4_enabled = TRUE;
if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"Krb4Method", &t)))
d->method = t;
else
d->method = K4_METHOD_AUTO;
if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"DefaultLifetime", &t)))
d->lifetime = t;
else
d->lifetime = 10 * 60 * 60;
if (d->nc->n_identities > 0 &&
d->nc->identities[0]) {
if (KHM_SUCCEEDED(kcdb_identity_get_config(d->nc->identities[0],
0,
&csp_ident))) {
khc_open_space(csp_ident, CSNAME_KRB4CRED, 0, &csp_k4);
if (csp_k4) {
if (KHM_SUCCEEDED(khc_read_int32(csp_k4, L"Krb4NewCreds", &t)))
d->k4_enabled = !!t;
if (KHM_SUCCEEDED(khc_read_int32(csp_k4, L"Krb4Method", &t)))
d->method = t;
khc_close_space(csp_k4);
}
khc_close_space(csp_ident);
}
if (d->k4_enabled) {
d->k4_enabled = k4_should_identity_get_k4(d->nc->identities[0]);
}
} else {
d->k4_enabled = FALSE;
}
if (d->method < 0 || d->method > K4_METHOD_K524)
d->method = K4_METHOD_AUTO;
}
void k4_write_identity_data(k4_dlg_data * d) {
khm_handle csp_ident = NULL;
khm_handle csp_k4 = NULL;
if (d->nc->n_identities > 0 &&
d->nc->identities[0] &&
KHM_SUCCEEDED(kcdb_identity_get_config(d->nc->identities[0],
KHM_FLAG_CREATE,
&csp_ident))) {
khc_open_space(csp_ident, CSNAME_KRB4CRED,
KHM_FLAG_CREATE | KCONF_FLAG_WRITEIFMOD,
&csp_k4);
if (csp_k4) {
khc_write_int32(csp_k4, L"Krb4NewCreds", !!d->k4_enabled);
khc_write_int32(csp_k4, L"Krb4Method", d->method);
khc_close_space(csp_k4);
}
khc_close_space(csp_ident);
}
}
void k4_handle_wmnc_notify(k4_dlg_data * d,
WPARAM wParam,
LPARAM lParam) {
switch(HIWORD(wParam)) {
case WMNC_UPDATE_CREDTEXT:
{
if (d->nct->credtext) {
PFREE(d->nct->credtext);
d->nct->credtext = NULL;
}
if (d->nc->n_identities > 0 &&
d->nc->identities[0]) {
khm_int32 flags = 0;
wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
wchar_t * atsign;
wchar_t * realm;
khm_size cb;
kcdb_identity_get_flags(d->nc->identities[0], &flags);
if (!(flags & KCDB_IDENT_FLAG_VALID)) {
break;
}
cb = sizeof(idname);
kcdb_identity_get_name(d->nc->identities[0], idname,
&cb);
atsign = wcsrchr(idname, L'@');
if (atsign == NULL || !atsign[1])
break;
realm = ++atsign;
if (d->k4_enabled) {
wchar_t wmethod[128];
wchar_t wfmt[128];
wchar_t wct[512];
LoadString(hResModule, IDS_CT_TGTFOR,
wfmt, ARRAYLENGTH(wfmt));
if (d->method == K4_METHOD_AUTO)
LoadString(hResModule, IDS_METHOD_AUTO, wmethod,
ARRAYLENGTH(wmethod));
else if (d->method == K4_METHOD_PASSWORD)
LoadString(hResModule, IDS_METHOD_PWD, wmethod,
ARRAYLENGTH(wmethod));
else if (d->method == K4_METHOD_K524)
LoadString(hResModule, IDS_METHOD_K524, wmethod,
ARRAYLENGTH(wmethod));
else {
assert(FALSE);
}
StringCbPrintf(wct, sizeof(wct), wfmt, realm, wmethod);
StringCbLength(wct, sizeof(wct), &cb);
cb += sizeof(wchar_t);
d->nct->credtext = PMALLOC(cb);
StringCbCopy(d->nct->credtext, cb, wct);
} else {
wchar_t wct[256];
LoadString(hResModule, IDS_CT_DISABLED,
wct, ARRAYLENGTH(wct));
StringCbLength(wct, sizeof(wct), &cb);
cb += sizeof(wchar_t);
d->nct->credtext = PMALLOC(cb);
StringCbCopy(d->nct->credtext, cb, wct);
}
}
}
break;
case WMNC_IDENTITY_CHANGE:
k4_read_identity_data(d);
k4_update_display(d, TRUE);
break;
case WMNC_CREDTEXT_LINK:
{
wchar_t wid[KHUI_MAXCCH_HTLINK_FIELD];
wchar_t * wids;
khui_htwnd_link * l;
l = (khui_htwnd_link *) lParam;
StringCchCopyN(wid, ARRAYLENGTH(wid), l->id, l->id_len);
wids = wcschr(wid, L':');
if (!wids)
break;
else
wids++;
if (!wcscmp(wids, L"Enable")) {
d->k4_enabled = TRUE;
k4_update_display(d, TRUE);
khui_cw_enable_type(d->nc, credtype_id_krb4, TRUE);
}
}
break;
}
}
INT_PTR CALLBACK k4_nc_dlg_proc(HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam) {
k4_dlg_data * d;
switch(uMsg) {
case WM_INITDIALOG:
{
d = PMALLOC(sizeof(*d));
ZeroMemory(d, sizeof(*d));
d->nc = (khui_new_creds *) lParam;
khui_cw_find_type(d->nc, credtype_id_krb4, &d->nct);
#pragma warning(push)
#pragma warning(disable: 4244)
SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM) d);
#pragma warning(pop)
d->nct->aux = (LPARAM) d;
d->hwnd = hwnd;
d->k4_enabled = TRUE;
d->method = K4_METHOD_AUTO;
k4_update_display(d, TRUE);
}
break;
case WM_COMMAND:
{
if (HIWORD(wParam) == BN_CLICKED) {
d = (k4_dlg_data *) (LONG_PTR)
GetWindowLongPtr(hwnd, DWLP_USER);
if (d == NULL)
break;
k4_update_data(d);
if (LOWORD(wParam) == IDC_NCK4_OBTAIN) {
k4_update_display(d, TRUE);
}
return TRUE;
}
}
break;
case KHUI_WM_NC_NOTIFY:
{
d = (k4_dlg_data *) (LONG_PTR)
GetWindowLongPtr(hwnd, DWLP_USER);
if (d == NULL)
break;
k4_handle_wmnc_notify(d, wParam, lParam);
}
break;
case WM_DESTROY:
{
d = (k4_dlg_data *) (LONG_PTR)
GetWindowLongPtr(hwnd, DWLP_USER);
if (d == NULL)
break;
d->nct->aux = 0;
PFREE(d);
SetWindowLongPtr(hwnd, DWLP_USER, 0);
}
break;
}
return FALSE;
}
khm_int32
krb4_msg_newcred(khm_int32 msg_type, khm_int32 msg_subtype,
khm_ui_4 uparam, void * vparam) {
switch(msg_subtype) {
case KMSG_CRED_NEW_CREDS:
{
khui_new_creds * nc;
khui_new_creds_by_type * nct;
khm_size cbsize;
wchar_t wbuf[256];
nc = (khui_new_creds *) vparam;
nct = PMALLOC(sizeof(*nct));
#ifdef DEBUG
assert(nct);
#endif
ZeroMemory(nct, sizeof(*nct));
nct->type = credtype_id_krb4;
nct->ordinal = 3;
LoadString(hResModule, IDS_NC_K4_SHORT,
wbuf, ARRAYLENGTH(wbuf));
StringCbLength(wbuf, sizeof(wbuf), &cbsize);
cbsize += sizeof(wchar_t);
nct->name = PMALLOC(cbsize);
StringCbCopy(nct->name, cbsize, wbuf);
nct->type_deps[nct->n_type_deps++] = credtype_id_krb5;
nct->h_module = hResModule;
nct->dlg_proc = k4_nc_dlg_proc;
nct->dlg_template = MAKEINTRESOURCE(IDD_NC_KRB4);
khui_cw_add_type(nc, nct);
}
break;
case KMSG_CRED_RENEW_CREDS:
{
khui_new_creds * nc;
khui_new_creds_by_type * nct;
khm_size cbsize;
wchar_t wbuf[256];
nc = (khui_new_creds *) vparam;
if (!nc->ctx.identity)
break;
nct = PMALLOC(sizeof(*nct));
#ifdef DEBUG
assert(nct);
#endif
ZeroMemory(nct, sizeof(*nct));
nct->type = credtype_id_krb4;
nct->ordinal = 3;
LoadString(hResModule, IDS_NC_K4_SHORT,
wbuf, ARRAYLENGTH(wbuf));
StringCbLength(wbuf, sizeof(wbuf), &cbsize);
cbsize += sizeof(wchar_t);
nct->name = PMALLOC(cbsize);
StringCbCopy(nct->name, cbsize, wbuf);
nct->type_deps[nct->n_type_deps++] = credtype_id_krb5;
khui_cw_add_type(nc, nct);
}
break;
case KMSG_CRED_DIALOG_SETUP:
break;
case KMSG_CRED_PROCESS:
{
khui_new_creds * nc;
khui_new_creds_by_type * nct = NULL;
khm_handle ident = NULL;
k4_dlg_data * d = NULL;
long code = 0;
wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
khm_size cb;
nc = (khui_new_creds *) vparam;
if (KHM_FAILED(khui_cw_find_type(nc, credtype_id_krb4, &nct)))
break;
if (nc->subtype == KMSG_CRED_NEW_CREDS ||
nc->subtype == KMSG_CRED_RENEW_CREDS) {
khm_int32 method;
if (nc->subtype == KMSG_CRED_NEW_CREDS) {
d = (k4_dlg_data *) nct->aux;
if (!d ||
nc->n_identities == 0 ||
nc->identities[0] == NULL ||
nc->result != KHUI_NC_RESULT_PROCESS) {
khui_cw_set_response(nc, credtype_id_krb4,
KHUI_NC_RESPONSE_SUCCESS |
KHUI_NC_RESPONSE_EXIT);
break;
}
if (!d->k4_enabled) {
k4_write_identity_data(d);
khui_cw_set_response(nc, credtype_id_krb4,
KHUI_NC_RESPONSE_SUCCESS |
KHUI_NC_RESPONSE_EXIT);
break;
}
method = d->method;
ident = nc->identities[0];
cb = sizeof(idname);
kcdb_identity_get_name(ident, idname, &cb);
_begin_task(0);
_report_mr2(KHERR_NONE, MSG_K4_NEW_CREDS,
_cstr(ident), _int32(method));
_resolve();
_describe();
} else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {
if ((nc->ctx.scope == KHUI_SCOPE_IDENT &&
nc->ctx.identity != NULL) ||
(nc->ctx.scope == KHUI_SCOPE_CREDTYPE &&
nc->ctx.cred_type == credtype_id_krb4 &&
nc->ctx.identity != NULL) ||
(nc->ctx.scope == KHUI_SCOPE_CRED &&
nc->ctx.cred_type == credtype_id_krb4 &&
nc->ctx.identity != NULL &&
nc->ctx.cred != NULL)) {
ident = nc->ctx.identity;
if (!k4_should_identity_get_k4(ident)) {
_reportf(L"Kerberos 4 is not enabled for this identity. Skipping");
khui_cw_set_response(nc, credtype_id_krb4,
KHUI_NC_RESPONSE_FAILED |
KHUI_NC_RESPONSE_EXIT);
break;
}
} else {
_reportf(L"Kerberos 4 is not within renewal scope. Skipping");
khui_cw_set_response(nc, credtype_id_krb4,
KHUI_NC_RESPONSE_FAILED |
KHUI_NC_RESPONSE_EXIT);
break;
}
method = K4_METHOD_K524;
_begin_task(0);
cb = sizeof(idname);
kcdb_identity_get_name(ident, idname, &cb);
_report_mr2(KHERR_NONE, MSG_K4_RENEW_CREDS,
_cstr(ident), _int32(method));
_resolve();
_describe();
} else {
assert(FALSE);
break;
}
if ((method == K4_METHOD_AUTO ||
method == K4_METHOD_K524) &&
khui_cw_type_succeeded(nc, credtype_id_krb5)) {
khm_handle tgt;
FILETIME ft_prev;
FILETIME ft_new;
khm_size cb;
_report_mr0(KHERR_INFO, MSG_K4_TRY_K524);
tgt = khm_krb4_find_tgt(NULL, ident);
if (tgt) {
cb = sizeof(ft_prev);
if (KHM_FAILED(kcdb_cred_get_attr(tgt,
KCDB_ATTR_EXPIRE,
NULL,
&ft_prev,
&cb)))
ZeroMemory(&ft_prev, sizeof(ft_prev));
kcdb_cred_release(tgt);
}
code = khm_convert524(ident);
_reportf(L"khm_convert524 returns code %d", code);
if (code == 0) {
khui_cw_set_response(nc, credtype_id_krb4,
KHUI_NC_RESPONSE_SUCCESS |
KHUI_NC_RESPONSE_EXIT);
if (nc->subtype == KMSG_CRED_NEW_CREDS) {
assert(d != NULL);
k4_write_identity_data(d);
} else if (nc->subtype == KMSG_CRED_RENEW_CREDS &&
(nc->ctx.scope == KHUI_SCOPE_CREDTYPE ||
nc->ctx.scope == KHUI_SCOPE_CRED)) {
khm_krb4_list_tickets();
tgt = khm_krb4_find_tgt(NULL, ident);
if (tgt) {
cb = sizeof(ft_new);
ZeroMemory(&ft_new, sizeof(ft_new));
kcdb_cred_get_attr(tgt,
KCDB_ATTR_EXPIRE,
NULL,
&ft_new,
&cb);
kcdb_cred_release(tgt);
}
if (!tgt ||
CompareFileTime(&ft_new,
&ft_prev) <= 0) {
khui_action_context ctx;
_reportf(L"Renewal of Krb4 creds failed to get a longer TGT. Triggering identity renewal");
khui_context_create(&ctx,
KHUI_SCOPE_IDENT,
nc->ctx.identity,
KCDB_CREDTYPE_INVALID,
NULL);
khui_action_trigger(KHUI_ACTION_RENEW_CRED,
&ctx);
khui_context_release(&ctx);
}
}
_end_task();
break;
} else if (method == K4_METHOD_K524) {
khui_cw_set_response(nc, credtype_id_krb4,
KHUI_NC_RESPONSE_FAILED |
KHUI_NC_RESPONSE_EXIT);
if (nc->subtype == KMSG_CRED_RENEW_CREDS &&
(nc->ctx.scope == KHUI_SCOPE_CREDTYPE ||
nc->ctx.scope == KHUI_SCOPE_CRED)) {
khui_action_context ctx;
_reportf(L"Renewal of Krb4 creds failed using k524. Triggerring identity renewal.");
khui_context_create(&ctx,
KHUI_SCOPE_IDENT,
nc->ctx.identity,
KCDB_CREDTYPE_INVALID,
NULL);
khui_action_trigger(KHUI_ACTION_RENEW_CRED,
&ctx);
khui_context_release(&ctx);
}
_end_task();
break;
}
}
if (method == K4_METHOD_AUTO ||
method == K4_METHOD_PASSWORD) {
khm_size n_prompts = 0;
khm_size idx;
khm_size cb;
wchar_t wpwd[KHUI_MAXCCH_PROMPT_VALUE];
char pwd[KHUI_MAXCCH_PROMPT_VALUE];
wchar_t widname[KCDB_IDENT_MAXCCH_NAME];
char idname[KCDB_IDENT_MAXCCH_NAME];
char * aname = NULL;
char * inst = NULL;
char * realm = NULL;
assert(nc->subtype == KMSG_CRED_NEW_CREDS);
_report_mr0(KHERR_INFO, MSG_K4_TRY_PASSWORD);
code = TRUE;
khui_cw_get_prompt_count(nc, &n_prompts);
if (n_prompts == 0)
goto _skip_pwd;
for (idx = 0; idx < n_prompts; idx++) {
khui_new_creds_prompt * p;
if (KHM_FAILED(khui_cw_get_prompt(nc, idx, &p)))
continue;
if (p->type == KHUI_NCPROMPT_TYPE_PASSWORD)
break;
}
if (idx >= n_prompts) {
_reportf(L"Password prompt not found");
goto _skip_pwd;
}
khui_cw_sync_prompt_values(nc);
cb = sizeof(wpwd);
if (KHM_FAILED(khui_cw_get_prompt_value(nc, idx,
wpwd,
&cb))) {
_reportf(L"Failed to obtain password value");
goto _skip_pwd;
}
UnicodeStrToAnsi(pwd, sizeof(pwd), wpwd);
cb = sizeof(widname);
kcdb_identity_get_name(ident,
widname,
&cb);
UnicodeStrToAnsi(idname, sizeof(idname), widname);
{
char * atsign;
atsign = strchr(idname, '@');
if (atsign == NULL) {
_reportf(L"Identity name does not contain an '@'");
goto _skip_pwd;
}
*atsign++ = 0;
realm = atsign;
}
{
char * slash;
slash = strchr(idname, '/');
if (slash != NULL) {
*slash++ = 0;
inst = slash;
} else {
inst = "";
}
}
aname = idname;
code = khm_krb4_kinit(aname, inst, realm,
(long) d->lifetime, pwd);
_reportf(L"khm_krb4_kinit returns code %d", code);
_skip_pwd:
if (code) {
khui_cw_set_response(nc, credtype_id_krb4,
KHUI_NC_RESPONSE_EXIT |
KHUI_NC_RESPONSE_FAILED);
} else {
khui_cw_set_response(nc, credtype_id_krb4,
KHUI_NC_RESPONSE_EXIT |
KHUI_NC_RESPONSE_SUCCESS);
if (nc->subtype == KMSG_CRED_NEW_CREDS) {
assert(d != NULL);
k4_write_identity_data(d);
}
}
}
_end_task();
}
}
break;
case KMSG_CRED_END:
{
khui_new_creds * nc;
khui_new_creds_by_type * nct = NULL;
nc = (khui_new_creds *) vparam;
if (KHM_FAILED(khui_cw_find_type(nc, credtype_id_krb4, &nct)))
break;
khui_cw_del_type(nc, credtype_id_krb4);
if (nct->name)
PFREE(nct->name);
if (nct->credtext)
PFREE(nct->credtext);
PFREE(nct);
}
break;
}
return KHM_ERROR_SUCCESS;
}