smb-vc-reset-on-request-20090218
[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<windows.h>
28 #include<netidmgr.h>
29 #include<dynimport.h>
30 #include<krb5common.h>
31 #ifdef DEBUG
32 #include<assert.h>
33 #endif
34 #include<strsafe.h>
35
36 /**************************************/
37 /* khm_krb5_error():           */
38 /**************************************/
39 int 
40 khm_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName, 
41                  int FreeContextFlag, krb5_context * ctx, 
42                  krb5_ccache * cache)
43 {
44 #ifdef NO_KRB5
45     return 0;
46 #else
47
48 #ifdef SHOW_MESSAGE_IN_AN_ANNOYING_WAY
49     char message[256];
50     const char *errText;
51     int krb5Error = ((int)(rc & 255));  
52
53     errText = perror_message(rc);   
54     _snprintf(message, sizeof(message), 
55         "%s\n(Kerberos error %ld)\n\n%s failed", 
56         errText, 
57         krb5Error, 
58         FailedFunctionName);
59
60     MessageBoxA(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR | 
61         MB_TASKMODAL | 
62         MB_SETFOREGROUND);
63 #endif
64
65     if (FreeContextFlag == 1)
66     {
67         if (*ctx != NULL)
68         {
69             if (*cache != NULL) {
70                 pkrb5_cc_close(*ctx, *cache);
71                 *cache = NULL;
72             }
73
74             pkrb5_free_context(*ctx);
75             *ctx = NULL;
76         }
77     }
78
79     return rc;
80
81 #endif //!NO_KRB5
82 }
83
84 int 
85 khm_krb5_initialize(khm_handle ident, 
86                     krb5_context *ctx, 
87                     krb5_ccache *cache)
88 {
89 #ifdef NO_KRB5
90     return(0);
91 #else
92
93     LPCSTR          functionName;
94     int             freeContextFlag;
95     krb5_error_code     rc = 0;
96     krb5_flags          flags = KRB5_TC_OPENCLOSE;
97
98     if (pkrb5_init_context == NULL)
99         return 1;
100
101     if (*ctx == 0 && (rc = (*pkrb5_init_context)(ctx))) {
102         functionName = "krb5_init_context()";
103         freeContextFlag = 0;
104         goto on_error;
105     }
106
107     if(*cache == 0) {
108         wchar_t wccname[MAX_PATH];
109         khm_size cbwccname;
110
111         if(ident != NULL) {
112             cbwccname = sizeof(wccname);
113             do {
114                 char ccname[256];
115
116                 if(KHM_FAILED(kcdb_identity_get_attrib(ident, L"Krb5CCName",
117                                                        NULL, wccname,
118                                                        &cbwccname))) {
119                     cbwccname = sizeof(wccname);
120                     if (KHM_FAILED
121                         (khm_krb5_find_ccache_for_identity(ident,
122                                                            ctx,
123                                                            wccname,
124                                                            &cbwccname))) {
125 #ifdef DEBUG_LIKE_A_MADMAN
126                         assert(FALSE);
127 #endif
128                         break;
129                     }
130                 }
131
132                 if(UnicodeStrToAnsi(ccname, sizeof(ccname), wccname) == 0)
133                     break;
134
135                 if((*pkrb5_cc_resolve)(*ctx, ccname, cache)) {
136                     functionName = "krb5_cc_resolve()";
137                     freeContextFlag = 1;
138                     goto on_error;
139                 }
140             } while(FALSE);
141         }
142
143 #ifndef FAILOVER_TO_DEFAULT_CCACHE
144         rc = 1;
145 #endif
146         if (*cache == 0
147 #ifdef FAILOVER_TO_DEFAULT_CCACHE
148             && (rc = (*pkrb5_cc_default)(*ctx, cache))
149 #endif
150             ) {
151             functionName = "krb5_cc_default()";
152             freeContextFlag = 1;
153             goto on_error;
154         }
155     }
156
157 #ifdef KRB5_TC_NOTICKET
158     flags = KRB5_TC_NOTICKET;
159 #endif
160
161     if ((rc = (*pkrb5_cc_set_flags)(*ctx, *cache, flags)))
162     {
163         if (rc != KRB5_FCC_NOFILE && rc != KRB5_CC_NOTFOUND)
164             khm_krb5_error(rc, "krb5_cc_set_flags()", 0, ctx, 
165             cache);
166         else if ((rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) && *ctx != NULL) {
167             if (*cache != NULL)
168                 (*pkrb5_cc_close)(*ctx, *cache);
169         }
170         return rc;
171     }
172     return 0;
173
174 on_error:
175     return khm_krb5_error(rc, functionName, freeContextFlag, ctx, cache);
176 #endif //!NO_KRB5
177 }
178
179 #define TIMET_TOLERANCE (60*5)
180
181 khm_int32 KHMAPI
182 khm_get_identity_expiration_time(krb5_context ctx, krb5_ccache cc, 
183                                  khm_handle ident, 
184                                  krb5_timestamp * pexpiration)
185 {
186     krb5_principal principal = 0;
187     char * princ_name = NULL;
188     krb5_creds creds;
189     krb5_error_code code;
190     krb5_error_code cc_code;
191     krb5_cc_cursor cur;
192     krb5_timestamp now, expiration = 0;
193
194     wchar_t w_ident_name[KCDB_IDENT_MAXCCH_NAME];
195     char    ident_name[KCDB_IDENT_MAXCCH_NAME];
196     khm_size cb;
197
198     khm_int32 rv = KHM_ERROR_NOT_FOUND;
199
200     if (!ctx || !cc || !ident || !pexpiration)
201         return KHM_ERROR_GENERAL;
202
203     code = pkrb5_cc_get_principal(ctx, cc, &principal);
204
205     if ( code )
206         return KHM_ERROR_INVALID_PARAM;
207
208     cb = sizeof(w_ident_name);
209     kcdb_identity_get_name(ident, w_ident_name, &cb);
210     UnicodeStrToAnsi(ident_name, sizeof(ident_name), w_ident_name);
211
212     code = pkrb5_unparse_name(ctx, principal, &princ_name);
213
214     /* compare principal to ident. */
215
216     if ( code || !princ_name ||
217          strcmp(princ_name, ident_name) ) {
218         if (princ_name)
219             pkrb5_free_unparsed_name(ctx, princ_name);
220         pkrb5_free_principal(ctx, principal);
221         return KHM_ERROR_UNKNOWN;
222     }
223
224     pkrb5_free_unparsed_name(ctx, princ_name);
225     pkrb5_free_principal(ctx, principal);
226
227     code = pkrb5_timeofday(ctx, &now);
228
229     if (code)
230         return KHM_ERROR_UNKNOWN;
231
232     cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
233
234     while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {
235         krb5_data * c0 = krb5_princ_name(ctx, creds.server);
236         krb5_data * c1  = krb5_princ_component(ctx, creds.server, 1);
237         krb5_data * r = krb5_princ_realm(ctx, creds.server);
238
239         if ( c0 && c1 && r && c1->length == r->length && 
240              !strncmp(c1->data,r->data,r->length) &&
241              !strncmp("krbtgt",c0->data,c0->length) ) {
242
243             /* we have a TGT, check for the expiration time.
244              * if it is valid and renewable, use the renew time 
245              */
246
247             if (!(creds.ticket_flags & TKT_FLG_INVALID) &&
248                 creds.times.starttime < (now + TIMET_TOLERANCE) && 
249                 (creds.times.endtime + TIMET_TOLERANCE) > now) {
250                 expiration = creds.times.endtime;
251
252                 if ((creds.ticket_flags & TKT_FLG_RENEWABLE) && 
253                     (creds.times.renew_till > creds.times.endtime)) {
254                     expiration = creds.times.renew_till;
255                 }
256             }
257         }
258     }
259
260     if (cc_code == KRB5_CC_END) {
261         cc_code = pkrb5_cc_end_seq_get(ctx, cc, &cur);
262         rv = KHM_ERROR_SUCCESS;
263         *pexpiration = expiration;
264     }
265
266     return rv;
267 }
268
269 khm_int32 KHMAPI
270 khm_krb5_find_ccache_for_identity(khm_handle ident, krb5_context *pctx,
271                                   void * buffer, khm_size * pcbbuf)
272 {
273     krb5_context        ctx = 0;
274     krb5_ccache         cache = 0;
275     krb5_error_code     code;
276     apiCB *             cc_ctx = 0;
277     struct _infoNC **   pNCi = NULL;
278     int                 i;
279     khm_int32           t;
280     wchar_t *           ms = NULL;
281     khm_size            cb;
282     krb5_timestamp      expiration = 0;
283     krb5_timestamp      best_match_expiration = 0;
284     char                best_match_ccname[256] = "";
285     khm_handle          csp_params = NULL;
286     khm_handle          csp_plugins = NULL;
287
288     if (!buffer || !pcbbuf)
289     return KHM_ERROR_GENERAL;
290
291     ctx = *pctx;
292
293     if (!pcc_initialize ||
294         !pcc_get_NC_info ||
295         !pcc_free_NC_info ||
296         !pcc_shutdown)
297         goto _skip_cc_iter;
298
299     code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL);
300     if (code)
301         goto _exit;
302
303     code = pcc_get_NC_info(cc_ctx, &pNCi);
304
305     if (code) 
306         goto _exit;
307
308     for(i=0; pNCi[i]; i++) {
309         if (pNCi[i]->vers != CC_CRED_V5)
310             continue;
311
312         code = (*pkrb5_cc_resolve)(ctx, pNCi[i]->name, &cache);
313         if (code)
314             continue;
315
316         /* need a function to check the cache for the identity
317          * and determine if it has valid tickets.  If it has 
318          * the right identity and valid tickets, store the 
319          * expiration time and the cache name.  If it has the
320          * right identity but no valid tickets, store the ccache
321          * name and an expiration time of zero.  if it does not
322          * have the right identity don't save the name.
323          * 
324          * Keep searching to find the best cache available.
325          */
326
327         if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, 
328                                                            ident, 
329                                                            &expiration))) {
330             if ( expiration > best_match_expiration ) {
331                 best_match_expiration = expiration;
332                 StringCbCopyA(best_match_ccname, 
333                               sizeof(best_match_ccname),
334                               "API:");
335                 StringCbCatA(best_match_ccname,
336                              sizeof(best_match_ccname),
337                              pNCi[i]->name);
338                 expiration = 0;
339             }
340         }
341
342         if(ctx != NULL && cache != NULL)
343             (*pkrb5_cc_close)(ctx, cache);
344         cache = 0;
345     }
346
347  _skip_cc_iter:
348
349     if (KHM_SUCCEEDED(kmm_get_plugins_config(0, &csp_plugins))) {
350         khc_open_space(csp_plugins, L"Krb5Cred\\Parameters",  0, &csp_params);
351         khc_close_space(csp_plugins);
352         csp_plugins = NULL;
353     }
354
355 #ifdef DEBUG
356     if (csp_params == NULL) {
357         assert(FALSE);
358     }
359 #endif
360
361     if (csp_params &&
362         KHM_SUCCEEDED(khc_read_int32(csp_params, L"MsLsaList", &t)) && t) {
363         code = (*pkrb5_cc_resolve)(ctx, "MSLSA:", &cache);
364         if (code == 0 && cache) {
365             if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, 
366                                                                ident, 
367                                                                &expiration))) {
368                 if ( expiration > best_match_expiration ) {
369                     best_match_expiration = expiration;
370                     StringCbCopyA(best_match_ccname, sizeof(best_match_ccname),
371                                   "MSLSA:");
372                     expiration = 0;
373                 }
374             }
375         }
376
377         if (ctx != NULL && cache != NULL)
378             (*pkrb5_cc_close)(ctx, cache);
379
380         cache = 0;
381     }
382
383     if (csp_params &&
384         khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)
385         == KHM_ERROR_TOO_LONG &&
386         cb > sizeof(wchar_t) * 2) {
387
388         wchar_t * t;
389         char ccname[MAX_PATH + 6];
390
391         ms = PMALLOC(cb);
392
393 #ifdef DEBUG
394         assert(ms);
395 #endif
396
397         khc_read_multi_string(csp_params, L"FileCCList", ms, &cb);
398         for(t = ms; t && *t; t = multi_string_next(t)) {
399             StringCchPrintfA(ccname, ARRAYLENGTH(ccname),
400                              "FILE:%S", t);
401
402             code = (*pkrb5_cc_resolve)(ctx, ccname, &cache);
403             if (code)
404                 continue;
405
406             if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, 
407                                                                ident, 
408                                                                &expiration))) {
409                 if ( expiration > best_match_expiration ) {
410                     best_match_expiration = expiration;
411                     StringCbCopyA(best_match_ccname,
412                                   sizeof(best_match_ccname),
413                                   ccname);
414                     expiration = 0;
415                 }
416             }
417
418             if (ctx != NULL && cache != NULL)
419                 (*pkrb5_cc_close)(ctx, cache);
420             cache = 0;
421         }
422
423         PFREE(ms);
424     }
425  _exit:
426     if (csp_params)
427         khc_close_space(csp_params);
428
429     if (pNCi)
430         (*pcc_free_NC_info)(cc_ctx, &pNCi);
431
432     if (cc_ctx)
433         (*pcc_shutdown)(&cc_ctx);
434
435     if (best_match_ccname[0]) {
436         
437         if (*pcbbuf = AnsiStrToUnicode((wchar_t *)buffer, 
438                                        *pcbbuf,
439                                        best_match_ccname)) {
440
441             *pcbbuf = (*pcbbuf + 1) * sizeof(wchar_t);
442
443             return KHM_ERROR_SUCCESS;
444         }
445
446     }
447
448     return KHM_ERROR_GENERAL;
449 }