Windows: fix checked UNICODE build of talocale
[openafs.git] / src / WINNT / netidmgr_plugin / krb5common.c
1 /*
2  * Copyright (c) 2005 Massachusetts Institute of Technology
3  *
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:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
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.
23  */
24
25 /* $Id$ */
26
27 #include <afsconfig.h>
28 #include <afs/param.h>
29
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>
38
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)
46 {
47 #ifdef NO_KRB5
48     return 0;
49 #else
50
51 #ifdef SHOW_MESSAGE_IN_AN_ANNOYING_WAY
52     char message[256];
53     const char *errText;
54     int krb5Error = ((int)(rc & 255));  
55
56     if (pkrb5_get_error_message)
57         errText = pkrb5_get_error_message(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 (pkrb5_free_error_message)
66         pkrb5_free_error_message(errText);
67
68     MessageBoxA(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR | 
69         MB_TASKMODAL | 
70         MB_SETFOREGROUND);
71 #endif
72
73     if (FreeContextFlag == 1)
74     {
75         if (*ctx != NULL)
76         {
77             if (*cache != NULL) {
78                 pkrb5_cc_close(*ctx, *cache);
79                 *cache = NULL;
80             }
81
82             pkrb5_free_context(*ctx);
83             *ctx = NULL;
84         }
85     }
86
87     return rc;
88
89 #endif //!NO_KRB5
90 }
91
92 int 
93 khm_krb5_initialize(khm_handle ident, 
94                     krb5_context *ctx, 
95                     krb5_ccache *cache)
96 {
97 #ifdef NO_KRB5
98     return(0);
99 #else
100
101     LPCSTR          functionName;
102     int             freeContextFlag;
103     krb5_error_code     rc = 0;
104     krb5_flags          flags = KRB5_TC_OPENCLOSE;
105
106     if (pkrb5_init_context == NULL)
107         return 1;
108
109     if (*ctx == 0 && (rc = (*pkrb5_init_context)(ctx))) {
110         functionName = "krb5_init_context()";
111         freeContextFlag = 0;
112         goto on_error;
113     }
114
115     if(*cache == 0) {
116         wchar_t wccname[MAX_PATH];
117         khm_size cbwccname;
118
119         if(ident != NULL) {
120             cbwccname = sizeof(wccname);
121             do {
122                 char ccname[256];
123
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;
137                     }
138                 }
139
140                 if(UnicodeStrToAnsi(ccname, sizeof(ccname), wccname) == 0)
141                     break;
142
143                 if((*pkrb5_cc_resolve)(*ctx, ccname, cache)) {
144                     functionName = "krb5_cc_resolve()";
145                     freeContextFlag = 1;
146                     goto on_error;
147                 }
148             } while(FALSE);
149         }
150
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
158             ) {
159             functionName = "krb5_cc_default()";
160             freeContextFlag = 1;
161             goto on_error;
162         }
163     }
164
165 #ifdef KRB5_TC_NOTICKET
166     flags = KRB5_TC_NOTICKET;
167 #endif
168
169     if ((rc = (*pkrb5_cc_set_flags)(*ctx, *cache, flags)))
170     {
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);
177         }
178         return rc;
179     }
180     return 0;
181
182 on_error:
183     return khm_krb5_error(rc, functionName, freeContextFlag, ctx, cache);
184 #endif //!NO_KRB5
185 }
186
187 #define TIMET_TOLERANCE (60*5)
188
189 khm_int32 KHMAPI
190 khm_get_identity_expiration_time(krb5_context ctx, krb5_ccache cc, 
191                                  khm_handle ident, 
192                                  krb5_timestamp * pexpiration)
193 {
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;
201
202     wchar_t w_ident_name[KCDB_IDENT_MAXCCH_NAME];
203     char    ident_name[KCDB_IDENT_MAXCCH_NAME];
204     khm_size cb;
205
206     khm_int32 rv = KHM_ERROR_NOT_FOUND;
207
208     if (!ctx || !cc || !ident || !pexpiration)
209         return KHM_ERROR_GENERAL;
210
211     code = pkrb5_cc_get_principal(ctx, cc, &principal);
212
213     if ( code )
214         return KHM_ERROR_INVALID_PARAM;
215
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);
219
220     code = pkrb5_unparse_name(ctx, principal, &princ_name);
221
222     /* compare principal to ident. */
223
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;
230     }
231
232     pkrb5_free_unparsed_name(ctx, princ_name);
233     pkrb5_free_principal(ctx, principal);
234
235     code = pkrb5_timeofday(ctx, &now);
236
237     if (code)
238         return KHM_ERROR_UNKNOWN;
239
240     cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
241
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);
246
247         if ( c0 && c1 && r && c1->length == r->length && 
248              !strncmp(c1->data,r->data,r->length) &&
249              !strncmp("krbtgt",c0->data,c0->length) ) {
250
251             /* we have a TGT, check for the expiration time.
252              * if it is valid and renewable, use the renew time 
253              */
254
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;
259
260                 if ((creds.ticket_flags & TKT_FLG_RENEWABLE) && 
261                     (creds.times.renew_till > creds.times.endtime)) {
262                     expiration = creds.times.renew_till;
263                 }
264             }
265         }
266     }
267
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;
272     }
273
274     return rv;
275 }
276
277 khm_int32 KHMAPI
278 khm_krb5_find_ccache_for_identity(khm_handle ident, krb5_context *pctx,
279                                   void * buffer, khm_size * pcbbuf)
280 {
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;
295
296     if (!buffer || !pcbbuf)
297     return KHM_ERROR_GENERAL;
298
299     ctx = *pctx;
300
301     if (!pcc_initialize ||
302         !pcc_get_NC_info ||
303         !pcc_free_NC_info ||
304         !pcc_shutdown)
305         goto _skip_cc_iter;
306
307     code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL);
308     if (code)
309         goto _exit;
310
311     code = pcc_get_NC_info(cc_ctx, &pNCi);
312
313     if (code) 
314         goto _exit;
315
316     for(i=0; pNCi[i]; i++) {
317         if (pNCi[i]->vers != CC_CRED_V5)
318             continue;
319
320         code = (*pkrb5_cc_resolve)(ctx, pNCi[i]->name, &cache);
321         if (code)
322             continue;
323
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.
331          * 
332          * Keep searching to find the best cache available.
333          */
334
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;
347             }
348         }
349
350         if(ctx != NULL && cache != NULL)
351             (*pkrb5_cc_close)(ctx, cache);
352         cache = 0;
353     }
354
355  _skip_cc_iter:
356
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;
361     }
362
363 #ifdef DEBUG
364     if (csp_params == NULL) {
365         assert(FALSE);
366     }
367 #endif
368
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;
381                 }
382             }
383         }
384
385         if (ctx != NULL && cache != NULL)
386             (*pkrb5_cc_close)(ctx, cache);
387
388         cache = 0;
389     }
390
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) {
395
396         wchar_t * t;
397         char ccname[MAX_PATH + 6];
398
399         ms = PMALLOC(cb);
400
401 #ifdef DEBUG
402         assert(ms);
403 #endif
404
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);
409
410             code = (*pkrb5_cc_resolve)(ctx, ccname, &cache);
411             if (code)
412                 continue;
413
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;
423                 }
424             }
425
426             if (ctx != NULL && cache != NULL)
427                 (*pkrb5_cc_close)(ctx, cache);
428             cache = 0;
429         }
430
431         PFREE(ms);
432     }
433  _exit:
434     if (csp_params)
435         khc_close_space(csp_params);
436
437     if (pNCi)
438         (*pcc_free_NC_info)(cc_ctx, &pNCi);
439
440     if (cc_ctx)
441         (*pcc_shutdown)(&cc_ctx);
442
443     if (best_match_ccname[0]) {
444         
445         if (*pcbbuf = AnsiStrToUnicode((wchar_t *)buffer, 
446                                        *pcbbuf,
447                                        best_match_ccname)) {
448
449             *pcbbuf = (*pcbbuf + 1) * sizeof(wchar_t);
450
451             return KHM_ERROR_SUCCESS;
452         }
453
454     }
455
456     return KHM_ERROR_GENERAL;
457 }