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