Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / WINNT / netidmgr_plugin / krb5common.c
blob8212c802f7293775e3f74e5bc03fa5907fd6219d
1 /*
2 * Copyright (c) 2005 Massachusetts Institute of Technology
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
25 /* $Id$ */
27 #include <afsconfig.h>
28 #include <afs/param.h>
30 #include<windows.h>
31 #include<netidmgr.h>
32 #include<dynimport.h>
33 #include<krb5common.h>
34 #ifdef DEBUG
35 #include<assert.h>
36 #endif
37 #include<strsafe.h>
39 /**************************************/
40 /* khm_krb5_error(): */
41 /**************************************/
42 int
43 khm_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName,
44 int FreeContextFlag, krb5_context * ctx,
45 krb5_ccache * cache)
47 #ifdef NO_KRB5
48 return 0;
49 #else
51 #ifdef SHOW_MESSAGE_IN_AN_ANNOYING_WAY
52 char message[256];
53 const char *errText;
54 int krb5Error = ((int)(rc & 255));
56 if (*ctx && pkrb5_get_error_message)
57 errText = pkrb5_get_error_message(*ctx, rc);
58 else
59 errText = perror_message(rc);
60 _snprintf(message, sizeof(message),
61 "%s\n(Kerberos error %ld)\n\n%s failed",
62 errText,
63 krb5Error,
64 FailedFunctionName);
65 if (*ctx && pkrb5_free_error_message)
66 pkrb5_free_error_message(*ctx, errText);
68 MessageBoxA(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR |
69 MB_TASKMODAL |
70 MB_SETFOREGROUND);
71 #endif
73 if (FreeContextFlag == 1)
75 if (*ctx != NULL)
77 if (*cache != NULL) {
78 pkrb5_cc_close(*ctx, *cache);
79 *cache = NULL;
82 pkrb5_free_context(*ctx);
83 *ctx = NULL;
87 return rc;
89 #endif //!NO_KRB5
92 int
93 khm_krb5_initialize(khm_handle ident,
94 krb5_context *ctx,
95 krb5_ccache *cache)
97 #ifdef NO_KRB5
98 return(0);
99 #else
101 LPCSTR functionName;
102 int freeContextFlag;
103 krb5_error_code rc = 0;
104 krb5_flags flags = KRB5_TC_OPENCLOSE;
106 if (pkrb5_init_context == NULL)
107 return 1;
109 if (*ctx == 0 && (rc = (*pkrb5_init_context)(ctx))) {
110 functionName = "krb5_init_context()";
111 freeContextFlag = 0;
112 goto on_error;
115 if(*cache == 0) {
116 wchar_t wccname[MAX_PATH];
117 khm_size cbwccname;
119 if(ident != NULL) {
120 cbwccname = sizeof(wccname);
121 do {
122 char ccname[256];
124 if(KHM_FAILED(kcdb_identity_get_attrib(ident, L"Krb5CCName",
125 NULL, wccname,
126 &cbwccname))) {
127 cbwccname = sizeof(wccname);
128 if (KHM_FAILED
129 (khm_krb5_find_ccache_for_identity(ident,
130 ctx,
131 wccname,
132 &cbwccname))) {
133 #ifdef DEBUG_LIKE_A_MADMAN
134 assert(FALSE);
135 #endif
136 break;
140 if(UnicodeStrToAnsi(ccname, sizeof(ccname), wccname) == 0)
141 break;
143 if((*pkrb5_cc_resolve)(*ctx, ccname, cache)) {
144 functionName = "krb5_cc_resolve()";
145 freeContextFlag = 1;
146 goto on_error;
148 } while(FALSE);
151 #ifndef FAILOVER_TO_DEFAULT_CCACHE
152 rc = 1;
153 #endif
154 if (*cache == 0
155 #ifdef FAILOVER_TO_DEFAULT_CCACHE
156 && (rc = (*pkrb5_cc_default)(*ctx, cache))
157 #endif
159 functionName = "krb5_cc_default()";
160 freeContextFlag = 1;
161 goto on_error;
165 #ifdef KRB5_TC_NOTICKET
166 flags = KRB5_TC_NOTICKET;
167 #endif
169 if ((rc = (*pkrb5_cc_set_flags)(*ctx, *cache, flags)))
171 if (rc != KRB5_FCC_NOFILE && rc != KRB5_CC_NOTFOUND)
172 khm_krb5_error(rc, "krb5_cc_set_flags()", 0, ctx,
173 cache);
174 else if ((rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) && *ctx != NULL) {
175 if (*cache != NULL)
176 (*pkrb5_cc_close)(*ctx, *cache);
178 return rc;
180 return 0;
182 on_error:
183 return khm_krb5_error(rc, functionName, freeContextFlag, ctx, cache);
184 #endif //!NO_KRB5
187 #define TIMET_TOLERANCE (60*5)
189 khm_int32 KHMAPI
190 khm_get_identity_expiration_time(krb5_context ctx, krb5_ccache cc,
191 khm_handle ident,
192 krb5_timestamp * pexpiration)
194 krb5_principal principal = 0;
195 char * princ_name = NULL;
196 krb5_creds creds;
197 krb5_error_code code;
198 krb5_error_code cc_code;
199 krb5_cc_cursor cur;
200 krb5_timestamp now, expiration = 0;
202 wchar_t w_ident_name[KCDB_IDENT_MAXCCH_NAME];
203 char ident_name[KCDB_IDENT_MAXCCH_NAME];
204 khm_size cb;
206 khm_int32 rv = KHM_ERROR_NOT_FOUND;
208 if (!ctx || !cc || !ident || !pexpiration)
209 return KHM_ERROR_GENERAL;
211 code = pkrb5_cc_get_principal(ctx, cc, &principal);
213 if ( code )
214 return KHM_ERROR_INVALID_PARAM;
216 cb = sizeof(w_ident_name);
217 kcdb_identity_get_name(ident, w_ident_name, &cb);
218 UnicodeStrToAnsi(ident_name, sizeof(ident_name), w_ident_name);
220 code = pkrb5_unparse_name(ctx, principal, &princ_name);
222 /* compare principal to ident. */
224 if ( code || !princ_name ||
225 strcmp(princ_name, ident_name) ) {
226 if (princ_name)
227 pkrb5_free_unparsed_name(ctx, princ_name);
228 pkrb5_free_principal(ctx, principal);
229 return KHM_ERROR_UNKNOWN;
232 pkrb5_free_unparsed_name(ctx, princ_name);
233 pkrb5_free_principal(ctx, principal);
235 code = pkrb5_timeofday(ctx, &now);
237 if (code)
238 return KHM_ERROR_UNKNOWN;
240 cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
242 while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {
243 krb5_data * c0 = krb5_princ_name(ctx, creds.server);
244 krb5_data * c1 = krb5_princ_component(ctx, creds.server, 1);
245 krb5_data * r = krb5_princ_realm(ctx, creds.server);
247 if ( c0 && c1 && r && c1->length == r->length &&
248 !strncmp(c1->data,r->data,r->length) &&
249 !strncmp("krbtgt",c0->data,c0->length) ) {
251 /* we have a TGT, check for the expiration time.
252 * if it is valid and renewable, use the renew time
255 if (!(creds.ticket_flags & TKT_FLG_INVALID) &&
256 creds.times.starttime < (now + TIMET_TOLERANCE) &&
257 (creds.times.endtime + TIMET_TOLERANCE) > now) {
258 expiration = creds.times.endtime;
260 if ((creds.ticket_flags & TKT_FLG_RENEWABLE) &&
261 (creds.times.renew_till > creds.times.endtime)) {
262 expiration = creds.times.renew_till;
268 if (cc_code == KRB5_CC_END) {
269 cc_code = pkrb5_cc_end_seq_get(ctx, cc, &cur);
270 rv = KHM_ERROR_SUCCESS;
271 *pexpiration = expiration;
274 return rv;
277 khm_int32 KHMAPI
278 khm_krb5_find_ccache_for_identity(khm_handle ident, krb5_context *pctx,
279 void * buffer, khm_size * pcbbuf)
281 krb5_context ctx = 0;
282 krb5_ccache cache = 0;
283 krb5_error_code code;
284 apiCB * cc_ctx = 0;
285 struct _infoNC ** pNCi = NULL;
286 int i;
287 khm_int32 t;
288 wchar_t * ms = NULL;
289 khm_size cb;
290 krb5_timestamp expiration = 0;
291 krb5_timestamp best_match_expiration = 0;
292 char best_match_ccname[256] = "";
293 khm_handle csp_params = NULL;
294 khm_handle csp_plugins = NULL;
296 if (!buffer || !pcbbuf)
297 return KHM_ERROR_GENERAL;
299 ctx = *pctx;
301 if (!pcc_initialize ||
302 !pcc_get_NC_info ||
303 !pcc_free_NC_info ||
304 !pcc_shutdown)
305 goto _skip_cc_iter;
307 code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL);
308 if (code)
309 goto _exit;
311 code = pcc_get_NC_info(cc_ctx, &pNCi);
313 if (code)
314 goto _exit;
316 for(i=0; pNCi[i]; i++) {
317 if (pNCi[i]->vers != CC_CRED_V5)
318 continue;
320 code = (*pkrb5_cc_resolve)(ctx, pNCi[i]->name, &cache);
321 if (code)
322 continue;
324 /* need a function to check the cache for the identity
325 * and determine if it has valid tickets. If it has
326 * the right identity and valid tickets, store the
327 * expiration time and the cache name. If it has the
328 * right identity but no valid tickets, store the ccache
329 * name and an expiration time of zero. if it does not
330 * have the right identity don't save the name.
332 * Keep searching to find the best cache available.
335 if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache,
336 ident,
337 &expiration))) {
338 if ( expiration > best_match_expiration ) {
339 best_match_expiration = expiration;
340 StringCbCopyA(best_match_ccname,
341 sizeof(best_match_ccname),
342 "API:");
343 StringCbCatA(best_match_ccname,
344 sizeof(best_match_ccname),
345 pNCi[i]->name);
346 expiration = 0;
350 if(ctx != NULL && cache != NULL)
351 (*pkrb5_cc_close)(ctx, cache);
352 cache = 0;
355 _skip_cc_iter:
357 if (KHM_SUCCEEDED(kmm_get_plugins_config(0, &csp_plugins))) {
358 khc_open_space(csp_plugins, L"Krb5Cred\\Parameters", 0, &csp_params);
359 khc_close_space(csp_plugins);
360 csp_plugins = NULL;
363 #ifdef DEBUG
364 if (csp_params == NULL) {
365 assert(FALSE);
367 #endif
369 if (csp_params &&
370 KHM_SUCCEEDED(khc_read_int32(csp_params, L"MsLsaList", &t)) && t) {
371 code = (*pkrb5_cc_resolve)(ctx, "MSLSA:", &cache);
372 if (code == 0 && cache) {
373 if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache,
374 ident,
375 &expiration))) {
376 if ( expiration > best_match_expiration ) {
377 best_match_expiration = expiration;
378 StringCbCopyA(best_match_ccname, sizeof(best_match_ccname),
379 "MSLSA:");
380 expiration = 0;
385 if (ctx != NULL && cache != NULL)
386 (*pkrb5_cc_close)(ctx, cache);
388 cache = 0;
391 if (csp_params &&
392 khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)
393 == KHM_ERROR_TOO_LONG &&
394 cb > sizeof(wchar_t) * 2) {
396 wchar_t * t;
397 char ccname[MAX_PATH + 6];
399 ms = PMALLOC(cb);
401 #ifdef DEBUG
402 assert(ms);
403 #endif
405 khc_read_multi_string(csp_params, L"FileCCList", ms, &cb);
406 for(t = ms; t && *t; t = multi_string_next(t)) {
407 StringCchPrintfA(ccname, ARRAYLENGTH(ccname),
408 "FILE:%S", t);
410 code = (*pkrb5_cc_resolve)(ctx, ccname, &cache);
411 if (code)
412 continue;
414 if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache,
415 ident,
416 &expiration))) {
417 if ( expiration > best_match_expiration ) {
418 best_match_expiration = expiration;
419 StringCbCopyA(best_match_ccname,
420 sizeof(best_match_ccname),
421 ccname);
422 expiration = 0;
426 if (ctx != NULL && cache != NULL)
427 (*pkrb5_cc_close)(ctx, cache);
428 cache = 0;
431 PFREE(ms);
433 _exit:
434 if (csp_params)
435 khc_close_space(csp_params);
437 if (pNCi)
438 (*pcc_free_NC_info)(cc_ctx, &pNCi);
440 if (cc_ctx)
441 (*pcc_shutdown)(&cc_ctx);
443 if (best_match_ccname[0]) {
445 if (*pcbbuf = AnsiStrToUnicode((wchar_t *)buffer,
446 *pcbbuf,
447 best_match_ccname)) {
449 *pcbbuf = (*pcbbuf + 1) * sizeof(wchar_t);
451 return KHM_ERROR_SUCCESS;
456 return KHM_ERROR_GENERAL;