92a902be3c968120e293e605abbc2f62b08b1ca8
[openafs.git] / src / WINNT / client_creds / afskfw.c
1 /*
2  * Copyright (c) 2003 SkyRope, LLC
3  * All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without 
6  * modification, are permitted provided that the following conditions are met:
7  * 
8  * - Redistributions of source code must retain the above copyright notice, 
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright notice, 
11  *   this list of conditions and the following disclaimer in the documentation 
12  *   and/or other materials provided with the distribution.
13  * - Neither the name of Skyrope, LLC nor the names of its contributors may be 
14  *   used to endorse or promote products derived from this software without 
15  *   specific prior written permission from Skyrope, LLC.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * Portions of this code are derived from portions of the MIT
30  * Leash Ticket Manager and LoadFuncs utilities.  For these portions the
31  * following copyright applies.
32  *
33  * Copyright (c) 2003,2004 by the Massachusetts Institute of Technology.
34  * All rights reserved.
35  *
36  * Export of this software from the United States of America may
37  *   require a specific license from the United States Government.
38  *   It is the responsibility of any person or organization contemplating
39  *   export to obtain such a license before exporting.
40  *
41  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
42  * distribute this software and its documentation for any purpose and
43  * without fee is hereby granted, provided that the above copyright
44  * notice appear in all copies and that both that copyright notice and
45  * this permission notice appear in supporting documentation, and that
46  * the name of M.I.T. not be used in advertising or publicity pertaining
47  * to distribution of the software without specific, written prior
48  * permission.  Furthermore if you modify this software you must label
49  * your software as modified software and not distribute it in such a
50  * fashion that it might be confused with the original M.I.T. software.
51  * M.I.T. makes no representations about the suitability of
52  * this software for any purpose.  It is provided "as is" without express
53  * or implied warranty.
54  *
55  */
56
57
58 #define USE_MS2MIT
59 #define USE_KRB4
60 #include "afskfw-int.h"
61 #include "afskfw.h"
62 #include "creds.h"
63
64 #include <osilog.h>
65
66 /*
67  * TIMING _____________________________________________________________________
68  *
69  */
70
71 #define cminREMIND_TEST      1    // test every minute for expired creds
72 #define cminREMIND_WARN      15   // warn if creds expire in 15 minutes
73 #define cminRENEW            20   // renew creds when there are 20 minutes remaining
74 #define cminMINLIFE          30   // minimum life of Kerberos creds
75
76 #define c100ns1SECOND        (LONGLONG)10000000
77 #define cmsec1SECOND         1000
78 #define cmsec1MINUTE         60000
79 #define csec1MINUTE          60
80
81 /* Function Pointer Declarations for Delayed Loading */
82 // CCAPI
83 DECL_FUNC_PTR(cc_initialize);
84 DECL_FUNC_PTR(cc_shutdown);
85 DECL_FUNC_PTR(cc_get_NC_info);
86 DECL_FUNC_PTR(cc_free_NC_info);
87
88 // leash functions
89 DECL_FUNC_PTR(Leash_get_default_lifetime);
90 DECL_FUNC_PTR(Leash_get_default_forwardable);
91 DECL_FUNC_PTR(Leash_get_default_renew_till);
92 DECL_FUNC_PTR(Leash_get_default_noaddresses);
93 DECL_FUNC_PTR(Leash_get_default_proxiable);
94 DECL_FUNC_PTR(Leash_get_default_publicip);
95 DECL_FUNC_PTR(Leash_get_default_use_krb4);
96 DECL_FUNC_PTR(Leash_get_default_life_min);
97 DECL_FUNC_PTR(Leash_get_default_life_max);
98 DECL_FUNC_PTR(Leash_get_default_renew_min);
99 DECL_FUNC_PTR(Leash_get_default_renew_max);
100 DECL_FUNC_PTR(Leash_get_default_renewable);
101
102 // krb5 functions
103 DECL_FUNC_PTR(krb5_change_password);
104 DECL_FUNC_PTR(krb5_get_init_creds_opt_init);
105 DECL_FUNC_PTR(krb5_get_init_creds_opt_set_tkt_life);
106 DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life);
107 DECL_FUNC_PTR(krb5_get_init_creds_opt_set_forwardable);
108 DECL_FUNC_PTR(krb5_get_init_creds_opt_set_proxiable);
109 DECL_FUNC_PTR(krb5_get_init_creds_opt_set_address_list);
110 DECL_FUNC_PTR(krb5_get_init_creds_password);
111 DECL_FUNC_PTR(krb5_build_principal_ext);
112 DECL_FUNC_PTR(krb5_cc_get_name);
113 DECL_FUNC_PTR(krb5_cc_resolve);
114 DECL_FUNC_PTR(krb5_cc_default);
115 DECL_FUNC_PTR(krb5_cc_default_name);
116 DECL_FUNC_PTR(krb5_cc_set_default_name);
117 DECL_FUNC_PTR(krb5_cc_initialize);
118 DECL_FUNC_PTR(krb5_cc_destroy);
119 DECL_FUNC_PTR(krb5_cc_close);
120 DECL_FUNC_PTR(krb5_cc_store_cred);
121 DECL_FUNC_PTR(krb5_cc_copy_creds);
122 DECL_FUNC_PTR(krb5_cc_retrieve_cred);
123 DECL_FUNC_PTR(krb5_cc_get_principal);
124 DECL_FUNC_PTR(krb5_cc_start_seq_get);
125 DECL_FUNC_PTR(krb5_cc_next_cred);
126 DECL_FUNC_PTR(krb5_cc_end_seq_get);
127 DECL_FUNC_PTR(krb5_cc_remove_cred);
128 DECL_FUNC_PTR(krb5_cc_set_flags);
129 DECL_FUNC_PTR(krb5_cc_get_type);
130 DECL_FUNC_PTR(krb5_free_context);
131 DECL_FUNC_PTR(krb5_free_cred_contents);
132 DECL_FUNC_PTR(krb5_free_principal);
133 DECL_FUNC_PTR(krb5_get_in_tkt_with_password);
134 DECL_FUNC_PTR(krb5_init_context);
135 DECL_FUNC_PTR(krb5_parse_name);
136 DECL_FUNC_PTR(krb5_timeofday);
137 DECL_FUNC_PTR(krb5_timestamp_to_sfstring);
138 DECL_FUNC_PTR(krb5_unparse_name);
139 DECL_FUNC_PTR(krb5_get_credentials);
140 DECL_FUNC_PTR(krb5_mk_req);
141 DECL_FUNC_PTR(krb5_sname_to_principal);
142 DECL_FUNC_PTR(krb5_get_credentials_renew);
143 DECL_FUNC_PTR(krb5_free_data);
144 DECL_FUNC_PTR(krb5_free_data_contents);
145 DECL_FUNC_PTR(krb5_free_unparsed_name);
146 DECL_FUNC_PTR(krb5_os_localaddr);
147 DECL_FUNC_PTR(krb5_copy_keyblock_contents);
148 DECL_FUNC_PTR(krb5_copy_data);
149 DECL_FUNC_PTR(krb5_free_creds);
150 DECL_FUNC_PTR(krb5_build_principal);
151 DECL_FUNC_PTR(krb5_get_renewed_creds);
152 DECL_FUNC_PTR(krb5_get_default_config_files);
153 DECL_FUNC_PTR(krb5_free_config_files);
154 DECL_FUNC_PTR(krb5_get_default_realm);
155 DECL_FUNC_PTR(krb5_free_ticket);
156 DECL_FUNC_PTR(krb5_decode_ticket);
157 DECL_FUNC_PTR(krb5_get_host_realm);
158 DECL_FUNC_PTR(krb5_free_host_realm);
159 DECL_FUNC_PTR(krb5_free_addresses);
160 DECL_FUNC_PTR(krb5_c_random_make_octets);
161
162 // Krb524 functions
163 DECL_FUNC_PTR(krb524_init_ets);
164 DECL_FUNC_PTR(krb524_convert_creds_kdc);
165
166 // krb4 functions
167 DECL_FUNC_PTR(krb_get_cred);
168 DECL_FUNC_PTR(tkt_string);
169 DECL_FUNC_PTR(krb_get_tf_realm);
170 DECL_FUNC_PTR(krb_mk_req);
171
172 // ComErr functions
173 DECL_FUNC_PTR(com_err);
174 DECL_FUNC_PTR(error_message);
175
176 // Profile functions
177 DECL_FUNC_PTR(profile_init);
178 DECL_FUNC_PTR(profile_release);
179 DECL_FUNC_PTR(profile_get_subsection_names);
180 DECL_FUNC_PTR(profile_free_list);
181 DECL_FUNC_PTR(profile_get_string);
182 DECL_FUNC_PTR(profile_release_string);
183
184 // Service functions
185 DECL_FUNC_PTR(OpenSCManagerA);
186 DECL_FUNC_PTR(OpenServiceA);
187 DECL_FUNC_PTR(QueryServiceStatus);
188 DECL_FUNC_PTR(CloseServiceHandle);
189 #ifdef USE_MS2MIT
190 DECL_FUNC_PTR(LsaNtStatusToWinError);
191 #endif /* USE_MS2MIT */
192
193 #ifdef USE_MS2MIT
194 // LSA Functions
195 DECL_FUNC_PTR(LsaConnectUntrusted);
196 DECL_FUNC_PTR(LsaLookupAuthenticationPackage);
197 DECL_FUNC_PTR(LsaCallAuthenticationPackage);
198 DECL_FUNC_PTR(LsaFreeReturnBuffer);
199 DECL_FUNC_PTR(LsaGetLogonSessionData);
200 #endif /* USE_MS2MIT */
201
202 // AFS36 Token Functions
203 DECL_FUNC_PTR(ktc_ListTokens);
204 DECL_FUNC_PTR(ktc_GetToken);
205 DECL_FUNC_PTR(ktc_SetToken);
206 DECL_FUNC_PTR(ktc_ForgetAllTokens);
207
208 // AFS36 Config Functions
209 DECL_FUNC_PTR(cm_SearchCellFile);
210 DECL_FUNC_PTR(cm_GetRootCellName);
211
212 // CCAPI
213 FUNC_INFO ccapi_fi[] = {
214     MAKE_FUNC_INFO(cc_initialize),
215     MAKE_FUNC_INFO(cc_shutdown),
216     MAKE_FUNC_INFO(cc_get_NC_info),
217     MAKE_FUNC_INFO(cc_free_NC_info),
218     END_FUNC_INFO
219 };
220
221 FUNC_INFO leash_fi[] = {
222     MAKE_FUNC_INFO(Leash_get_default_lifetime),
223     MAKE_FUNC_INFO(Leash_get_default_renew_till),
224     MAKE_FUNC_INFO(Leash_get_default_forwardable),
225     MAKE_FUNC_INFO(Leash_get_default_noaddresses),
226     MAKE_FUNC_INFO(Leash_get_default_proxiable),
227     MAKE_FUNC_INFO(Leash_get_default_publicip),
228     MAKE_FUNC_INFO(Leash_get_default_use_krb4),
229     MAKE_FUNC_INFO(Leash_get_default_life_min),
230     MAKE_FUNC_INFO(Leash_get_default_life_max),
231     MAKE_FUNC_INFO(Leash_get_default_renew_min),
232     MAKE_FUNC_INFO(Leash_get_default_renew_max),
233     MAKE_FUNC_INFO(Leash_get_default_renewable),
234     END_FUNC_INFO
235 };
236
237 FUNC_INFO k5_fi[] = {
238     MAKE_FUNC_INFO(krb5_change_password),
239     MAKE_FUNC_INFO(krb5_get_init_creds_opt_init),
240     MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_tkt_life),
241     MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_renew_life),
242     MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_forwardable),
243     MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_proxiable),
244     MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_address_list),
245     MAKE_FUNC_INFO(krb5_get_init_creds_password),
246     MAKE_FUNC_INFO(krb5_build_principal_ext),
247     MAKE_FUNC_INFO(krb5_cc_get_name),
248     MAKE_FUNC_INFO(krb5_cc_resolve),
249     MAKE_FUNC_INFO(krb5_cc_default),
250     MAKE_FUNC_INFO(krb5_cc_default_name),
251     MAKE_FUNC_INFO(krb5_cc_set_default_name),
252     MAKE_FUNC_INFO(krb5_cc_initialize),
253     MAKE_FUNC_INFO(krb5_cc_destroy),
254     MAKE_FUNC_INFO(krb5_cc_close),
255     MAKE_FUNC_INFO(krb5_cc_copy_creds),
256     MAKE_FUNC_INFO(krb5_cc_store_cred),
257     MAKE_FUNC_INFO(krb5_cc_retrieve_cred),
258     MAKE_FUNC_INFO(krb5_cc_get_principal),
259     MAKE_FUNC_INFO(krb5_cc_start_seq_get),
260     MAKE_FUNC_INFO(krb5_cc_next_cred),
261     MAKE_FUNC_INFO(krb5_cc_end_seq_get),
262     MAKE_FUNC_INFO(krb5_cc_remove_cred),
263     MAKE_FUNC_INFO(krb5_cc_set_flags),
264     MAKE_FUNC_INFO(krb5_cc_get_type),
265     MAKE_FUNC_INFO(krb5_free_context),
266     MAKE_FUNC_INFO(krb5_free_cred_contents),
267     MAKE_FUNC_INFO(krb5_free_principal),
268     MAKE_FUNC_INFO(krb5_get_in_tkt_with_password),
269     MAKE_FUNC_INFO(krb5_init_context),
270     MAKE_FUNC_INFO(krb5_parse_name),
271     MAKE_FUNC_INFO(krb5_timeofday),
272     MAKE_FUNC_INFO(krb5_timestamp_to_sfstring),
273     MAKE_FUNC_INFO(krb5_unparse_name),
274     MAKE_FUNC_INFO(krb5_get_credentials),
275     MAKE_FUNC_INFO(krb5_mk_req),
276     MAKE_FUNC_INFO(krb5_sname_to_principal),
277     MAKE_FUNC_INFO(krb5_get_credentials_renew),
278     MAKE_FUNC_INFO(krb5_free_data),
279     MAKE_FUNC_INFO(krb5_free_data_contents),
280     MAKE_FUNC_INFO(krb5_free_unparsed_name),
281     MAKE_FUNC_INFO(krb5_os_localaddr),
282     MAKE_FUNC_INFO(krb5_copy_keyblock_contents),
283     MAKE_FUNC_INFO(krb5_copy_data),
284     MAKE_FUNC_INFO(krb5_free_creds),
285     MAKE_FUNC_INFO(krb5_build_principal),
286     MAKE_FUNC_INFO(krb5_get_renewed_creds),
287     MAKE_FUNC_INFO(krb5_free_addresses),
288     MAKE_FUNC_INFO(krb5_get_default_config_files),
289     MAKE_FUNC_INFO(krb5_free_config_files),
290     MAKE_FUNC_INFO(krb5_get_default_realm),
291     MAKE_FUNC_INFO(krb5_free_ticket),
292     MAKE_FUNC_INFO(krb5_decode_ticket),
293     MAKE_FUNC_INFO(krb5_get_host_realm),
294     MAKE_FUNC_INFO(krb5_free_host_realm),
295     MAKE_FUNC_INFO(krb5_free_addresses),
296     MAKE_FUNC_INFO(krb5_c_random_make_octets),
297     END_FUNC_INFO
298 };
299
300 FUNC_INFO k4_fi[] = {
301     MAKE_FUNC_INFO(krb_get_cred),
302     MAKE_FUNC_INFO(krb_get_tf_realm),
303     MAKE_FUNC_INFO(krb_mk_req),
304     MAKE_FUNC_INFO(tkt_string),
305     END_FUNC_INFO
306 };
307
308 FUNC_INFO k524_fi[] = {
309     MAKE_FUNC_INFO(krb524_init_ets),
310     MAKE_FUNC_INFO(krb524_convert_creds_kdc),
311     END_FUNC_INFO
312 };
313
314 FUNC_INFO profile_fi[] = {
315         MAKE_FUNC_INFO(profile_init),
316         MAKE_FUNC_INFO(profile_release),
317         MAKE_FUNC_INFO(profile_get_subsection_names),
318         MAKE_FUNC_INFO(profile_free_list),
319         MAKE_FUNC_INFO(profile_get_string),
320         MAKE_FUNC_INFO(profile_release_string),
321         END_FUNC_INFO
322 };
323
324 FUNC_INFO ce_fi[] = {
325     MAKE_FUNC_INFO(com_err),
326     MAKE_FUNC_INFO(error_message),
327     END_FUNC_INFO
328 };
329
330 FUNC_INFO service_fi[] = {
331     MAKE_FUNC_INFO(OpenSCManagerA),
332     MAKE_FUNC_INFO(OpenServiceA),
333     MAKE_FUNC_INFO(QueryServiceStatus),
334     MAKE_FUNC_INFO(CloseServiceHandle),
335 #ifdef USE_MS2MIT
336     MAKE_FUNC_INFO(LsaNtStatusToWinError),
337 #endif /* USE_MS2MIT */
338     END_FUNC_INFO
339 };
340
341 #ifdef USE_MS2MIT
342 FUNC_INFO lsa_fi[] = {
343     MAKE_FUNC_INFO(LsaConnectUntrusted),
344     MAKE_FUNC_INFO(LsaLookupAuthenticationPackage),
345     MAKE_FUNC_INFO(LsaCallAuthenticationPackage),
346     MAKE_FUNC_INFO(LsaFreeReturnBuffer),
347     MAKE_FUNC_INFO(LsaGetLogonSessionData),
348     END_FUNC_INFO
349 };
350 #endif /* USE_MS2MIT */
351
352 FUNC_INFO afst_fi[] = {
353     MAKE_FUNC_INFO(ktc_ListTokens),
354     MAKE_FUNC_INFO(ktc_GetToken),
355     MAKE_FUNC_INFO(ktc_SetToken),
356     MAKE_FUNC_INFO(ktc_ForgetAllTokens),
357     END_FUNC_INFO
358 };
359
360 FUNC_INFO afsc_fi[] = {
361     MAKE_FUNC_INFO(cm_SearchCellFile),
362     MAKE_FUNC_INFO(cm_GetRootCellName),
363     END_FUNC_INFO
364 };
365
366 /* Static Prototypes */
367 static char *afs_realm_of_cell(afsconf_cell *);
368 static long get_cellconfig_callback(void *, struct sockaddr_in *, char *);
369 static int get_cellconfig(char *, afsconf_cell *, char *);
370 static krb5_error_code KRB5_CALLCONV KRB5_prompter( krb5_context context,
371            void *data, const char *name, const char *banner, int num_prompts,
372            krb5_prompt prompts[]);
373
374
375 /* Static Declarations */
376 static int                inited = 0;
377 static int                mid_cnt = 0;
378 static struct textField * mid_tb = NULL;
379 static HINSTANCE hKrb5 = 0;
380 static HINSTANCE hKrb4 = 0;
381 static HINSTANCE hKrb524 = 0;
382 #ifdef USE_MS2MIT
383 static HINSTANCE hSecur32 = 0;
384 #endif /* USE_MS2MIT */
385 static HINSTANCE hAdvApi32 = 0;
386 static HINSTANCE hAfsTokens = 0;
387 static HINSTANCE hAfsConf = 0;
388 static HINSTANCE hComErr = 0;
389 static HINSTANCE hService = 0;
390 static HINSTANCE hProfile = 0;
391 static HINSTANCE hLeash = 0;
392 static HINSTANCE hCCAPI = 0;
393 static struct principal_ccache_data * princ_cc_data = NULL;
394 static struct cell_principal_map    * cell_princ_map = NULL;
395
396 void
397 KFW_initialize(void)
398 {
399     static int inited = 0;
400     if ( !inited ) {
401         inited = 1;
402         LoadFuncs(KRB5_DLL, k5_fi, &hKrb5, 0, 1, 0, 0);
403         LoadFuncs(KRB4_DLL, k4_fi, &hKrb5, 0, 1, 0, 0);
404         LoadFuncs(COMERR_DLL, ce_fi, &hComErr, 0, 0, 1, 0);
405         LoadFuncs(SERVICE_DLL, service_fi, &hService, 0, 1, 0, 0);
406 #ifdef USE_MS2MIT
407         LoadFuncs(SECUR32_DLL, lsa_fi, &hSecur32, 0, 1, 1, 1);
408 #endif /* USE_MS2MIT */
409         LoadFuncs(KRB524_DLL, k524_fi, &hKrb524, 0, 1, 1, 1);
410         LoadFuncs(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0);
411         LoadFuncs(AFSTOKENS_DLL, afst_fi, &hAfsTokens, 0, 1, 0, 0);
412         LoadFuncs(AFSCONF_DLL, afsc_fi, &hAfsConf, 0, 1, 0, 0);
413         LoadFuncs(LEASH_DLL, leash_fi, &hLeash, 0, 1, 0, 0);
414         LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0);
415
416         if ( KFW_is_available() ) {
417             char rootcell[MAXCELLCHARS+1];
418 #ifdef USE_MS2MIT
419             KFW_import_windows_lsa();
420 #endif /* USE_MS2MIT */
421             KFW_import_ccache_data();
422                     KFW_AFS_renew_expiring_credentials();
423
424             /* WIN32 NOTE: no way to get max chars */
425             if (!pcm_GetRootCellName(rootcell))
426                 KFW_AFS_renew_token_for_cell(rootcell);
427         }
428     }
429 }
430
431 void
432 KFW_cleanup(void)
433 {
434     if (hKrb5)
435         FreeLibrary(hKrb5);
436     if (hKrb4)
437         FreeLibrary(hKrb4);
438     if (hProfile)
439         FreeLibrary(hProfile);
440     if (hAfsTokens)
441         FreeLibrary(hAfsTokens);
442     if (hAfsConf)
443         FreeLibrary(hAfsConf);
444     if (hComErr)
445         FreeLibrary(hComErr);
446     if (hService)
447         FreeLibrary(hService);
448 #ifdef USE_MS2MIT
449     if (hSecur32)
450         FreeLibrary(hSecur32);
451 #endif /* USE_MS2MIT */
452     if (hKrb524)
453         FreeLibrary(hKrb524);
454     if (hLeash)
455         FreeLibrary(hLeash);
456     if (hCCAPI)
457         FreeLibrary(hCCAPI);
458 }
459
460 int 
461 KFW_is_available(void)
462 {
463     KFW_initialize();
464     if ( hKrb5 && hComErr && hService && 
465 #ifdef USE_MS2MIT
466          hSecur32 && 
467 #endif /* USE_MS2MIT */
468          hKrb524 &&
469          hProfile && hAfsTokens && hAfsConf )
470         return TRUE;
471     return FALSE;
472 }
473
474 int 
475 KRB5_error(krb5_error_code rc, LPCSTR FailedFunctionName, 
476                  int FreeContextFlag, krb5_context * ctx, 
477                  krb5_ccache * cache)
478 {
479     char message[256];
480     const char *errText;
481     int krb5Error = ((int)(rc & 255));  
482     
483     /*
484     switch (krb5Error)
485     {
486         // Wrong password
487         case 31:
488         case 8:
489             return;
490     }
491     */
492         
493     errText = perror_message(rc);   
494     _snprintf(message, sizeof(message), 
495               "%s\n(Kerberos error %ld)\n\n%s failed", 
496               errText, 
497               krb5Error, 
498               FailedFunctionName);
499
500     if ( IsDebuggerPresent() )
501         OutputDebugString(message);
502
503     MessageBox(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR | 
504                MB_TASKMODAL | 
505                MB_SETFOREGROUND);
506     if (FreeContextFlag == 1)
507     {
508         if (ctx && *ctx != NULL)
509         {
510             if (cache && *cache != NULL) {
511                 pkrb5_cc_close(*ctx, *cache);
512                                 *cache = NULL;
513                         }
514         
515             pkrb5_free_context(*ctx);
516                         *ctx = NULL;
517         }
518     }
519
520     return rc;
521 }
522
523 void
524 KFW_AFS_update_princ_ccache_data(krb5_context ctx, krb5_ccache cc, int lsa)
525 {
526     struct principal_ccache_data * next = princ_cc_data;
527     krb5_principal principal = 0;
528     char * pname = NULL;
529     const char * ccname = NULL;
530     krb5_error_code code = 0;
531     krb5_error_code cc_code = 0;
532     krb5_cc_cursor cur;
533     krb5_creds creds;
534     krb5_flags flags=0;
535     krb5_timestamp now;
536
537     if (ctx == 0 || cc == 0)
538         return;
539
540     code = pkrb5_cc_get_principal(ctx, cc, &principal);
541     if ( code ) return;
542
543     code = pkrb5_unparse_name(ctx, principal, &pname);
544     if ( code ) goto cleanup;
545
546     ccname = pkrb5_cc_get_name(ctx, cc);
547     if (!ccname) goto cleanup;
548
549     // Search the existing list to see if we have a match 
550     if ( next ) {
551         for ( ; next ; next = next->next ) {
552             if ( !strcmp(next->principal,pname) && !strcmp(next->ccache_name, ccname) )
553                 break;
554         }
555     } 
556
557     // If not, match add a new node to the beginning of the list and assign init it
558     if ( !next ) {
559         next = (struct principal_ccache_data *) malloc(sizeof(struct principal_ccache_data));
560         next->next = princ_cc_data;
561         princ_cc_data = next;
562         next->principal = _strdup(pname);
563         next->ccache_name = _strdup(ccname);
564         next->from_lsa = lsa;
565         next->expired = 1;
566         next->expiration_time = 0;
567         next->renew = 0;
568     }
569
570     flags = 0;  // turn off OPENCLOSE mode
571     code = pkrb5_cc_set_flags(ctx, cc, flags);
572     if ( code ) goto cleanup;
573
574     code = pkrb5_timeofday(ctx, &now);
575
576     cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
577
578     while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {
579         if ( creds.ticket_flags & TKT_FLG_INITIAL ) {
580             int valid;
581             // we found the ticket we are looking for
582             // check validity of timestamp 
583             // We add a 5 minutes fudge factor to compensate for potential
584             // clock skew errors between the KDC and client OS
585
586             valid = ((creds.times.starttime > 0) &&
587                      now >= (creds.times.starttime - 300) &&
588                      now < (creds.times.endtime + 300) &&
589                      !(creds.ticket_flags & TKT_FLG_INVALID));
590
591             if ( next->from_lsa) {
592                 next->expired = 0;
593                 next->expiration_time = creds.times.endtime;
594                 next->renew = 1;
595             } else if ( valid ) {
596                 next->expired = 0;
597                 next->expiration_time = creds.times.endtime;
598                 next->renew = (creds.times.renew_till > creds.times.endtime) && 
599                                (creds.ticket_flags & TKT_FLG_RENEWABLE);
600             } else {
601                 next->expired = 1;
602                 next->expiration_time = 0;
603                 next->renew = 0;
604             }
605
606             pkrb5_free_cred_contents(ctx, &creds);
607             cc_code = KRB5_CC_END;
608             break;
609         }
610         pkrb5_free_cred_contents(ctx, &creds);
611     }
612
613     if (cc_code == KRB5_CC_END) {
614         code = pkrb5_cc_end_seq_get(ctx, cc, &cur);
615         if (code) goto cleanup;
616     }
617
618   cleanup:
619     flags = KRB5_TC_OPENCLOSE;  //turn on OPENCLOSE
620     code = pkrb5_cc_set_flags(ctx, cc, flags);
621
622     if ( pname )
623         pkrb5_free_unparsed_name(ctx,pname);
624     if ( principal )
625         pkrb5_free_principal(ctx,principal);
626 }   
627
628 int
629 KFW_AFS_find_ccache_for_principal(krb5_context ctx, char * principal, char **ccache, int valid_only)
630 {
631     struct principal_ccache_data * next = princ_cc_data;
632     char * response = NULL;
633
634     if ( !principal || !ccache )
635         return 0;
636
637     while ( next ) {
638         if ( (!valid_only || !next->expired) && !strcmp(next->principal,principal) ) {
639             if (response) {
640                 // we always want to prefer the MS Kerberos LSA cache or
641                 // the cache afscreds created specifically for the principal
642                 // if the current entry is either one, drop the previous find
643                 if ( next->from_lsa || !strcmp(next->ccache_name,principal) )
644                     free(response);
645             }
646             response = _strdup(next->ccache_name);
647             // MS Kerberos LSA is our best option so use it and quit
648             if ( next->from_lsa )   
649                 break;
650         }
651         next = next->next;
652     }
653
654     if ( response ) {
655         *ccache = response;
656         return 1;
657     }
658     return 0;
659 }
660
661 void 
662 KFW_AFS_delete_princ_ccache_data(krb5_context ctx, char * pname, char * ccname)
663 {
664     struct principal_ccache_data ** next = &princ_cc_data;
665
666     if ( !pname && !ccname )
667         return;
668
669     while ( (*next) ) {
670         if ( !strcmp((*next)->principal,pname) || 
671              !strcmp((*next)->ccache_name,ccname) ) {
672             void * temp;
673             free((*next)->principal);
674             free((*next)->ccache_name);
675             temp = (*next);
676             (*next) = (*next)->next;
677             free(temp);
678         }
679     }
680 }
681
682 void 
683 KFW_AFS_update_cell_princ_map(krb5_context ctx, char * cell, char *pname, int active)
684 {
685     struct cell_principal_map * next = cell_princ_map;
686
687     // Search the existing list to see if we have a match 
688     if ( next ) {
689         for ( ; next ; next = next->next ) {
690             if ( !strcmp(next->cell, cell) ) {
691                 if ( !strcmp(next->principal,pname) ) {
692                     next->active = active;
693                                         break;
694                 } else {
695                     // OpenAFS currently has a restriction of one active token per cell
696                     // Therefore, whenever we update the table with a new active cell we
697                     // must mark all of the other principal to cell entries as inactive.
698                     if (active)
699                         next->active = 0;
700                 }
701             }
702         }
703     } 
704
705     // If not, match add a new node to the beginning of the list and assign init it
706     if ( !next ) {
707         next = (struct cell_principal_map *) malloc(sizeof(struct cell_principal_map));
708         next->next = cell_princ_map;
709         cell_princ_map = next;
710         next->principal = _strdup(pname);
711         next->cell = _strdup(cell);
712         next->active = active;
713     }
714 }
715
716 void 
717 KFW_AFS_delete_cell_princ_maps(krb5_context ctx, char * pname, char * cell)
718 {
719     struct cell_principal_map ** next = &cell_princ_map;
720
721     if ( !pname && !cell )
722         return;
723
724     while ( (*next) ) {
725         if ( !strcmp((*next)->principal,pname) || 
726              !strcmp((*next)->cell,cell) ) {
727             void * temp;
728             free((*next)->principal);
729             free((*next)->cell);
730             temp = (*next);
731             (*next) = (*next)->next;
732             free(temp);
733         }
734     }
735 }
736
737 // Returns (if possible) a principal which has been known in 
738 // the past to have been used to obtain tokens for the specified
739 // cell.  
740 // TODO: Attempt to return one which has not yet expired by checking
741 // the principal/ccache data
742 int
743 KFW_AFS_find_principals_for_cell(krb5_context ctx, char * cell, char **principals[], int active_only)
744 {
745     struct cell_principal_map * next_map = cell_princ_map;
746     const char * princ = NULL;
747     int count = 0, i;
748
749     if ( !cell )
750         return 0;
751
752     while ( next_map ) {
753         if ( (!active_only || next_map->active) && !strcmp(next_map->cell,cell) ) {
754             count++;
755         }
756         next_map = next_map->next;
757     }
758
759     if ( !principals )
760         return count;
761
762     *principals = (char **) malloc(sizeof(char *) * count);
763     for ( next_map = cell_princ_map, i=0 ; next_map && i<count; next_map = next_map->next )
764     {
765         if ( (!active_only || next_map->active) && !strcmp(next_map->cell,cell) ) {
766             (*principals)[i++] = _strdup(next_map->principal);
767         }
768     }
769     return count;
770 }
771
772 int
773 KFW_AFS_find_cells_for_princ(krb5_context ctx, char * pname, char **cells[], int active_only)
774 {
775     int     count = 0, i;
776     struct cell_principal_map * next_map = cell_princ_map;
777     const char * princ = NULL;
778     
779     if ( !pname )
780         return 0;
781
782     while ( next_map ) {
783         if ( (!active_only || next_map->active) && !strcmp(next_map->principal,pname) ) {
784             count++;
785         }
786         next_map = next_map->next;
787     }
788
789     if ( !cells )
790         return count;
791
792     *cells = (char **) malloc(sizeof(char *) * count);
793     for ( next_map = cell_princ_map, i=0 ; next_map && i<count; next_map = next_map->next )
794     {
795         if ( (!active_only || next_map->active) && !strcmp(next_map->principal,pname) ) {
796             (*cells)[i++] = _strdup(next_map->cell);
797         }
798     }
799     return count;
800 }
801
802 /* Given a principal return an existing ccache or create one and return */
803 int
804 KFW_get_ccache(krb5_context alt_ctx, krb5_principal principal, krb5_ccache * cc)
805 {
806     krb5_context ctx;
807     char * pname = 0;
808     char * ccname = 0;
809     krb5_error_code code;
810
811     if ( alt_ctx ) {
812         ctx = alt_ctx;
813     } else {
814         code = pkrb5_init_context(&ctx);
815         if (code) goto cleanup;
816     }
817
818     if ( principal ) {
819         code = pkrb5_unparse_name(ctx, principal, &pname);
820         if (code) goto cleanup;
821
822         if ( !KFW_AFS_find_ccache_for_principal(ctx,pname,&ccname,TRUE) &&
823              !KFW_AFS_find_ccache_for_principal(ctx,pname,&ccname,FALSE)) {
824             ccname = (char *)malloc(strlen(pname) + 5);
825             sprintf(ccname,"API:%s",pname);
826         }
827         code = pkrb5_cc_resolve(ctx, ccname, cc);
828     } else {
829         code = pkrb5_cc_default(ctx, cc);
830         if (code) goto cleanup;
831     }
832
833   cleanup:
834     if (ccname)
835         free(ccname);
836     if (pname)
837         pkrb5_free_unparsed_name(ctx,pname);
838     if (ctx && (ctx != alt_ctx))
839         pkrb5_free_context(ctx);
840     return(code);
841 }
842
843 #ifdef USE_MS2MIT
844 // Import Microsoft Credentials into a new MIT ccache
845 void
846 KFW_import_windows_lsa(void)
847 {
848     krb5_context ctx = 0;
849     krb5_ccache  cc = 0;
850     krb5_principal princ = 0;
851     char * pname = NULL;
852     krb5_data *  realm;
853     krb5_error_code code;
854     char cell[128]="";
855     int i;
856          
857     if ( !MSLSA_IsKerberosLogon() )
858         return;
859
860     code = pkrb5_init_context(&ctx);
861     if (code) goto cleanup;
862
863     code = pkrb5_cc_resolve(ctx, LSA_CCNAME, &cc);
864     if (code) goto cleanup;
865
866     KFW_AFS_update_princ_ccache_data(ctx, cc, TRUE);
867
868     code = pkrb5_cc_get_principal(ctx, cc, &princ);
869     if ( code ) goto cleanup;
870
871     code = pkrb5_unparse_name(ctx,princ,&pname);
872     if ( code ) goto cleanup;
873
874     realm = krb5_princ_realm(ctx, princ);
875     for ( i=0; i<realm->length; i++ ) {
876         cell[i] = tolower(realm->data[i]);
877     }
878         cell[i] = '\0';
879
880     code = KFW_AFS_klog(ctx, cc, "afs", cell, realm->data, pLeash_get_default_lifetime());
881     if ( IsDebuggerPresent() ) {
882         char message[256];
883         sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
884         OutputDebugString(message);
885     }
886     if ( code ) goto cleanup;
887
888     KFW_AFS_update_cell_princ_map(ctx, cell, pname, TRUE);
889
890   cleanup:
891     if (pname)
892         pkrb5_free_unparsed_name(ctx,pname);
893     if (princ)
894         pkrb5_free_principal(ctx,princ);
895     if (cc)
896         pkrb5_cc_close(ctx,cc);
897     if (ctx)
898         pkrb5_free_context(ctx);
899 }
900 #endif /* USE_MS2MIT */
901
902 // If there are existing MIT credentials, copy them to a new
903 // ccache named after the principal
904
905 // Enumerate all existing MIT ccaches and construct entries
906 // in the principal_ccache table
907
908 // Enumerate all existing AFS Tokens and construct entries
909 // in the cell_principal table
910 void
911 KFW_import_ccache_data(void)
912 {
913     krb5_context ctx = 0;
914     krb5_ccache  cc = 0;
915     krb5_principal principal = 0;
916     krb5_creds creds;
917     krb5_error_code code;
918     krb5_error_code cc_code;
919     krb5_cc_cursor cur;
920     apiCB * cc_ctx = 0;
921     struct _infoNC ** pNCi = NULL;
922     int i, j, flags;
923
924     if ( !pcc_initialize )
925         return;
926
927     if ( IsDebuggerPresent() )
928         OutputDebugString("KFW_import_ccache_data()\n");
929
930     code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL);
931     if (code) goto cleanup;
932
933     code = pcc_get_NC_info(cc_ctx, &pNCi);
934     if (code) goto cleanup;
935
936     code = pkrb5_init_context(&ctx);
937     if (code) goto cleanup;
938
939     for ( i=0; pNCi[i]; i++ ) {
940         if ( pNCi[i]->vers != CC_CRED_V5 )
941             continue;
942         if ( IsDebuggerPresent() ) {
943             OutputDebugString("Principal: ");
944             OutputDebugString(pNCi[i]->principal);
945             OutputDebugString(" in ccache ");
946             OutputDebugString(pNCi[i]->name);
947             OutputDebugString("\n");
948         }
949         if ( strcmp(pNCi[i]->name,pNCi[i]->principal)
950              && strcmp(pNCi[i]->name,LSA_CCNAME) 
951              ) {
952             int found = 0;
953             krb5_ccache oldcc = 0;
954             for ( j=0; pNCi[j]; j++ ) {
955                 if (!strcmp(pNCi[j]->name,pNCi[i]->principal)) {
956                     found = 1;
957                     break;
958                 }
959             }
960             if (found)
961                 continue;
962
963             if ( IsDebuggerPresent() )
964                 OutputDebugString("copying ccache data to new ccache\n");
965
966             code = pkrb5_cc_resolve(ctx, pNCi[i]->principal, &cc);
967             if (code) goto loop_cleanup;
968             code = pkrb5_parse_name(ctx, pNCi[i]->principal, &principal);
969             if (code) goto loop_cleanup;
970             code = pkrb5_cc_initialize(ctx, cc, principal);
971             if (code) goto loop_cleanup;
972             code = pkrb5_cc_resolve(ctx, pNCi[i]->name, &oldcc);
973             if (code) goto loop_cleanup;
974             code = pkrb5_cc_copy_creds(ctx,oldcc,cc);
975                         if (code) {
976                                 code = pkrb5_cc_close(ctx,cc);
977                 cc = 0;
978                 code = pkrb5_cc_close(ctx,oldcc);
979                 cc = 0;
980                                 KRB5_error(code, "krb5_cc_copy_creds", 0, NULL, NULL);
981                                 continue;
982                         }
983             code = pkrb5_cc_close(ctx,oldcc);
984         } else {
985             code = pkrb5_cc_resolve(ctx, pNCi[i]->name, &cc);
986             if (code) goto loop_cleanup;
987         }
988
989         flags = 0;  // turn off OPENCLOSE mode
990         code = pkrb5_cc_set_flags(ctx, cc, flags);
991         if ( code ) goto cleanup;
992
993         KFW_AFS_update_princ_ccache_data(ctx, cc, !strcmp(pNCi[i]->name,LSA_CCNAME));
994
995         cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
996
997         while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {
998             krb5_data * sname = krb5_princ_name(ctx, creds.server);
999             krb5_data * cell  = krb5_princ_component(ctx, creds.server, 1);
1000             krb5_data * realm = krb5_princ_realm(ctx, creds.server);
1001             if ( sname && cell && !strcmp("afs",sname->data) ) {
1002                 struct ktc_principal    aserver;
1003                 struct ktc_principal    aclient;
1004                 struct ktc_token        atoken;
1005                 int active = TRUE;
1006
1007                 if ( IsDebuggerPresent() )  {
1008                     OutputDebugString("Found AFS ticket: ");
1009                     OutputDebugString(sname->data);
1010                     if ( cell->data ) {
1011                         OutputDebugString("/");
1012                         OutputDebugString(cell->data);
1013                     }
1014                     OutputDebugString("@");
1015                     OutputDebugString(realm->data);
1016                     OutputDebugString("\n");
1017                 }
1018
1019                 memset(&aserver, '\0', sizeof(aserver));
1020                 strcpy(aserver.name, sname->data);
1021                 strcpy(aserver.cell, cell->data);
1022
1023                 code = pktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
1024                 if (!code) {
1025                     // Found a token in AFS Client Server which matches
1026                     char pname[128], *p, *q;
1027                     for ( p=pname, q=aclient.name; *q; p++, q++)
1028                         *p = *q;
1029                     for ( *p++ = '@', q=aclient.cell; *q; p++, q++)
1030                         *p = toupper(*q);
1031                     *p = '\0';
1032
1033                     if ( IsDebuggerPresent() )  {
1034                         OutputDebugString("Found AFS token: ");
1035                         OutputDebugString(pname);
1036                         OutputDebugString("\n");
1037                     }
1038
1039                     if ( strcmp(pname,pNCi[i]->principal)  )
1040                         active = FALSE;
1041                     KFW_AFS_update_cell_princ_map(ctx, cell->data, pNCi[i]->principal, active);
1042                 } else {
1043                     // Attempt to import it
1044                     KFW_AFS_update_cell_princ_map(ctx, cell->data, pNCi[i]->principal, active);
1045
1046                     if ( IsDebuggerPresent() )  {
1047                         OutputDebugString("Calling KFW_AFS_klog() to obtain token\n");
1048                     }
1049
1050                     code = KFW_AFS_klog(ctx, cc, "afs", cell->data, realm->data, pLeash_get_default_lifetime());
1051                     if ( IsDebuggerPresent() ) {
1052                         char message[256];
1053                         sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1054                         OutputDebugString(message);
1055                     }
1056                 }
1057             } else if ( IsDebuggerPresent() ) {
1058                 OutputDebugString("Found ticket: ");
1059                 OutputDebugString(sname->data);
1060                 if ( cell && cell->data ) {
1061                     OutputDebugString("/");
1062                     OutputDebugString(cell->data);
1063                 }
1064                 OutputDebugString("@");
1065                 OutputDebugString(realm->data);
1066                 OutputDebugString("\n");
1067             }
1068             pkrb5_free_cred_contents(ctx, &creds);
1069         }
1070
1071         if (cc_code == KRB5_CC_END) {
1072             cc_code = pkrb5_cc_end_seq_get(ctx, cc, &cur);
1073             if (cc_code) goto loop_cleanup;
1074         }
1075
1076       loop_cleanup:
1077         flags = KRB5_TC_OPENCLOSE;  //turn on OPENCLOSE
1078         code = pkrb5_cc_set_flags(ctx, cc, flags);
1079         if (cc) {
1080             pkrb5_cc_close(ctx,cc);
1081             cc = 0;
1082         }
1083     }
1084
1085   cleanup:
1086     if (principal)
1087         pkrb5_free_principal(ctx,principal);
1088     if (ctx)
1089         pkrb5_free_context(ctx);
1090     if (pNCi)
1091         pcc_free_NC_info(cc_ctx, &pNCi);
1092     if (cc_ctx)
1093         pcc_shutdown(&cc_ctx);
1094 }
1095
1096
1097 int
1098 KFW_AFS_get_cred(char * username, 
1099                   char * instance, 
1100                   char * cell,
1101                   char * password,
1102                   int lifetime,
1103                   char ** reasonP )
1104 {
1105     krb5_context ctx = 0;
1106     krb5_ccache cc = 0;
1107     char * realm = 0;
1108     char ** realmlist = 0;
1109     krb5_principal principal = 0;
1110     char * pname = 0;
1111     krb5_error_code code;
1112         char local_cell[MAXCELLCHARS+1];
1113     char **cells = NULL;
1114     int  cell_count=0;
1115     afsconf_cell cellconfig;
1116
1117     if ( IsDebuggerPresent() ) {
1118         OutputDebugString("KFW_AFS_get_cred for token ");
1119         OutputDebugString(username);
1120         if ( instance ) {
1121             OutputDebugString("/");
1122             OutputDebugString(instance);
1123         }
1124         OutputDebugString("@");
1125         OutputDebugString(cell);
1126         OutputDebugString("\n");
1127     }
1128
1129     code = pkrb5_init_context(&ctx);
1130     if ( code ) goto cleanup;
1131
1132     code = get_cellconfig( cell, (void*)&cellconfig, local_cell);
1133     if ( code ) goto cleanup;
1134
1135     realm = afs_realm_of_cell(&cellconfig);  // do not free
1136
1137     if ( IsDebuggerPresent() ) {
1138         OutputDebugString("Realm: ");
1139         OutputDebugString(realm);
1140         OutputDebugString("\n");
1141     }
1142
1143     code = pkrb5_build_principal(ctx, &principal, strlen(realm),
1144                                  realm, username, 
1145                                  (instance && instance[0]) ? instance : NULL, 
1146                                  NULL);
1147
1148     code = KFW_get_ccache(ctx, principal, &cc);
1149     if ( code ) goto cleanup;
1150
1151     code = pkrb5_unparse_name(ctx, principal, &pname);
1152     if ( code ) goto cleanup;
1153
1154     if ( lifetime == 0 )
1155         lifetime = pLeash_get_default_lifetime();
1156
1157     code = KFW_kinit(ctx, cc, HWND_DESKTOP, 
1158                       pname, 
1159                       password,
1160                       lifetime,
1161                       pLeash_get_default_forwardable(),
1162                       pLeash_get_default_proxiable(),
1163                       pLeash_get_default_renewable() ? pLeash_get_default_renew_till() : 0,
1164                       pLeash_get_default_noaddresses(),
1165                       pLeash_get_default_publicip());
1166     if ( IsDebuggerPresent() ) {
1167         char message[256];
1168         sprintf(message,"KFW_kinit() returns: %d\n",code);
1169         OutputDebugString(message);
1170     }
1171     if ( code ) goto cleanup;
1172                    
1173     KFW_AFS_update_princ_ccache_data(ctx, cc, FALSE);
1174
1175     code = KFW_AFS_klog(ctx, cc, "afs", cell, realm, lifetime);
1176     if ( IsDebuggerPresent() ) {
1177         char message[256];
1178         sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1179         OutputDebugString(message);
1180     }
1181     if ( code ) goto cleanup;
1182
1183     KFW_AFS_update_cell_princ_map(ctx, cell, pname, TRUE);
1184
1185     // Attempt to obtain new tokens for other cells supported by the same 
1186     // principal
1187     cell_count = KFW_AFS_find_cells_for_princ(ctx, pname, &cells, TRUE);
1188     if ( cell_count > 1 ) {
1189         while ( cell_count-- ) {
1190             if ( strcmp(cells[cell_count],cell) ) {
1191                 if ( IsDebuggerPresent() ) {
1192                     char message[256];
1193                     sprintf(message,"found another cell for the same principal: %s\n",cell);
1194                     OutputDebugString(message);
1195                 }
1196                 code = get_cellconfig( cells[cell_count], (void*)&cellconfig, local_cell);
1197                 if ( code ) continue;
1198     
1199                 realm = afs_realm_of_cell(&cellconfig);  // do not free
1200                 if ( IsDebuggerPresent() ) {
1201                     OutputDebugString("Realm: ");
1202                     OutputDebugString(realm);
1203                     OutputDebugString("\n");
1204                 }
1205                 
1206                 code = KFW_AFS_klog(ctx, cc, "afs", cells[cell_count], realm, lifetime);
1207                 if ( IsDebuggerPresent() ) {
1208                     char message[256];
1209                     sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1210                     OutputDebugString(message);
1211                 }
1212             }
1213             free(cells[cell_count]);
1214         }
1215         free(cells);
1216     } else if ( cell_count == 1 ) {
1217         free(cells[0]);
1218         free(cells);
1219     }
1220
1221   cleanup:
1222     if ( pname )
1223         pkrb5_free_unparsed_name(ctx,pname);
1224     if ( cc )
1225         pkrb5_cc_close(ctx, cc);
1226
1227     if ( code && reasonP ) {
1228         *reasonP = (char *)perror_message(code);
1229     }
1230     return(code);
1231 }
1232
1233 int 
1234 KFW_AFS_destroy_tickets_for_cell(char * cell)
1235 {
1236     krb5_context                ctx = 0;
1237     krb5_error_code             code;
1238     int count;
1239     char ** principals = NULL;
1240
1241     if ( IsDebuggerPresent() ) {
1242         OutputDebugString("KFW_AFS_destroy_ticets_for_cell: ");
1243         OutputDebugString(cell);
1244         OutputDebugString("\n");
1245     }
1246
1247     code = pkrb5_init_context(&ctx);
1248     if (code) ctx = 0;
1249
1250     count = KFW_AFS_find_principals_for_cell(ctx, cell, &principals, FALSE);
1251     if ( count > 0 ) {
1252         krb5_principal      princ = 0;
1253         krb5_ccache                     cc  = 0;
1254
1255         while ( count-- ) {
1256             int cell_count = KFW_AFS_find_cells_for_princ(ctx, principals[count], NULL, TRUE);
1257             if ( cell_count > 1 ) {
1258                 // TODO - What we really should do here is verify whether or not any of the
1259                 // other cells which use this principal to obtain its credentials actually
1260                 // have valid tokens or not.  If they are currently using these credentials
1261                 // we will skip them.  For the time being we assume that if there is an active
1262                 // map in the table that they are actively being used.
1263                 goto loop_cleanup;
1264             }
1265
1266             code = pkrb5_parse_name(ctx, principals[count], &princ);
1267             if (code) goto loop_cleanup;
1268
1269             code = KFW_get_ccache(ctx, princ, &cc);
1270             if (code) goto loop_cleanup;
1271
1272             code = pkrb5_cc_destroy(ctx, cc);
1273             if (!code) cc = 0;
1274
1275           loop_cleanup:
1276             if ( cc ) {
1277                 pkrb5_cc_close(ctx, cc);
1278                 cc = 0;
1279             }
1280             if ( princ ) {
1281                 pkrb5_free_principal(ctx, princ);
1282                 princ = 0;
1283             }
1284
1285             KFW_AFS_update_cell_princ_map(ctx, cell, principals[count], FALSE);
1286             free(principals[count]);
1287         }
1288         free(principals);
1289     }
1290     pkrb5_free_context(ctx);
1291     return 0;
1292 }
1293
1294 int
1295 KFW_AFS_renew_expiring_credentials(void)
1296 {
1297     krb5_error_code                     code = 0;
1298     krb5_context                        ctx = 0;
1299     krb5_ccache                         cc = 0;
1300     krb5_timestamp now;
1301     struct principal_ccache_data * pcc_next = princ_cc_data;
1302     int cell_count;
1303     char ** cells=NULL;
1304     const char * realm = NULL;
1305     char local_cell[MAXCELLCHARS+1]="";
1306     afsconf_cell cellconfig;
1307
1308     if ( pcc_next == NULL ) // nothing to do
1309         return 0;
1310
1311     if ( IsDebuggerPresent() ) {
1312         OutputDebugString("KFW_AFS_renew_expiring_credentials\n");
1313     }
1314
1315     code = pkrb5_init_context(&ctx);
1316     if (code) goto cleanup;
1317
1318     code = pkrb5_timeofday(ctx, &now);
1319     if (code) goto cleanup; 
1320
1321     for ( ; pcc_next ; pcc_next = pcc_next->next ) {
1322         if ( pcc_next->expired ) 
1323             continue;
1324
1325         if ( now >= (pcc_next->expiration_time) ) {
1326             if ( !pcc_next->from_lsa ) {
1327                 pcc_next->expired = 1;
1328                 continue;
1329             }
1330         }
1331
1332         if ( pcc_next->renew && now >= (pcc_next->expiration_time - cminRENEW * csec1MINUTE) ) {
1333             code = pkrb5_cc_resolve(ctx, pcc_next->ccache_name, &cc);
1334             if ( code ) 
1335                                 goto loop_cleanup;
1336             code = KFW_renew(ctx,cc);
1337 #ifdef USE_MS2MIT
1338             if ( code && pcc_next->from_lsa)
1339                 goto loop_cleanup;
1340 #endif /* USE_MS2MIT */
1341
1342
1343             KFW_AFS_update_princ_ccache_data(ctx, cc, pcc_next->from_lsa);
1344             if (code) goto loop_cleanup;
1345
1346             // Attempt to obtain new tokens for other cells supported by the same 
1347             // principal
1348             cell_count = KFW_AFS_find_cells_for_princ(ctx, pcc_next->principal, &cells, TRUE);
1349             if ( cell_count > 0 ) {
1350                 while ( cell_count-- ) {
1351                     if ( IsDebuggerPresent() ) {
1352                         OutputDebugString("Cell: ");
1353                         OutputDebugString(cells[cell_count]);
1354                         OutputDebugString("\n");
1355                     }
1356                     code = get_cellconfig( cells[cell_count], (void*)&cellconfig, local_cell);
1357                     if ( code ) continue;
1358                     realm = afs_realm_of_cell(&cellconfig);  // do not free
1359                     if ( IsDebuggerPresent() ) {
1360                         OutputDebugString("Realm: ");
1361                         OutputDebugString(realm);
1362                         OutputDebugString("\n");
1363                     }
1364                     code = KFW_AFS_klog(ctx, cc, "afs", cells[cell_count], (char *)realm, pLeash_get_default_lifetime());
1365                     if ( IsDebuggerPresent() ) {
1366                         char message[256];
1367                         sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1368                         OutputDebugString(message);
1369                     }
1370                     free(cells[cell_count]);
1371                 }
1372                 free(cells);
1373             }
1374         }
1375
1376       loop_cleanup:
1377         if ( cc ) {
1378             pkrb5_cc_close(ctx,cc);
1379             cc = 0;
1380         }
1381     }
1382
1383   cleanup:
1384     if ( cc )
1385         pkrb5_cc_close(ctx,cc);
1386     if ( ctx )
1387         pkrb5_free_context(ctx);
1388
1389     return 0;
1390 }
1391
1392
1393 BOOL
1394 KFW_AFS_renew_token_for_cell(char * cell)
1395 {
1396     krb5_error_code                     code = 0;
1397     krb5_context                        ctx = 0;
1398     int count;
1399     char ** principals = NULL;
1400
1401     if ( IsDebuggerPresent() ) {
1402         OutputDebugString("KFW_AFS_renew_token_for_cell:");
1403         OutputDebugString(cell);
1404         OutputDebugString("\n");
1405     }
1406
1407     code = pkrb5_init_context(&ctx);
1408     if (code) goto cleanup;
1409
1410     count = KFW_AFS_find_principals_for_cell(ctx, cell, &principals, TRUE);
1411     if ( count > 0 ) {
1412         krb5_principal      princ = 0;
1413         krb5_principal      service = 0;
1414         krb5_creds          mcreds, creds;
1415         krb5_ccache                     cc  = 0;
1416         const char * realm = NULL;
1417         afsconf_cell cellconfig;
1418         char local_cell[MAXCELLCHARS+1];
1419
1420         while ( count-- ) {
1421             code = pkrb5_parse_name(ctx, principals[count], &princ);
1422             if (code) goto loop_cleanup;
1423
1424             code = KFW_get_ccache(ctx, princ, &cc);
1425             if (code) goto loop_cleanup;
1426
1427             code = get_cellconfig( cell, (void*)&cellconfig, local_cell);
1428             if ( code ) goto loop_cleanup;
1429
1430             realm = afs_realm_of_cell(&cellconfig);  // do not free
1431             if ( IsDebuggerPresent() ) {
1432                 OutputDebugString("Realm: ");
1433                 OutputDebugString(realm);
1434                 OutputDebugString("\n");
1435             }
1436
1437             code = pkrb5_build_principal(ctx, &service, strlen(realm),
1438                                           realm, "afs", cell, NULL);
1439             if (!code) {
1440                 memset(&mcreds, 0, sizeof(krb5_creds));
1441                 mcreds.client = princ;
1442                 mcreds.server = service;
1443
1444                 code = pkrb5_cc_retrieve_cred(ctx, cc, 0, &mcreds, &creds);
1445                 if (!code) {
1446                     if ( IsDebuggerPresent() ) {
1447                         char * cname, *sname;
1448                         pkrb5_unparse_name(ctx, creds.client, &cname);
1449                         pkrb5_unparse_name(ctx, creds.server, &sname);
1450                         OutputDebugString("Removing credential for client \"");
1451                         OutputDebugString(cname);
1452                         OutputDebugString("\" and service \"");
1453                         OutputDebugString(sname);
1454                         OutputDebugString("\"\n");
1455                         pkrb5_free_unparsed_name(ctx,cname);
1456                         pkrb5_free_unparsed_name(ctx,sname);
1457                     }
1458
1459                     code = pkrb5_cc_remove_cred(ctx, cc, 0, &creds);
1460                     pkrb5_free_principal(ctx, creds.client);
1461                     pkrb5_free_principal(ctx, creds.server);
1462                 }
1463             }
1464             code = KFW_AFS_klog(ctx, cc, "afs", cell, (char *)realm, pLeash_get_default_lifetime());
1465             if ( IsDebuggerPresent() ) {
1466                 char message[256];
1467                 sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1468                 OutputDebugString(message);
1469             }
1470
1471           loop_cleanup:
1472             if (cc) {
1473                 pkrb5_cc_close(ctx, cc);
1474                 cc = 0;
1475             }
1476             if (princ) {
1477                 pkrb5_free_principal(ctx, princ);
1478                 princ = 0;
1479             }
1480             if (service) {
1481                 pkrb5_free_principal(ctx, service);
1482                 princ = 0;
1483             }
1484
1485             KFW_AFS_update_cell_princ_map(ctx, cell, principals[count], code ? FALSE : TRUE);
1486             free(principals[count]);
1487         }
1488         free(principals);
1489     } else
1490         code = -1;      // we did not renew the tokens 
1491
1492   cleanup:
1493     pkrb5_free_context(ctx);
1494     return (code ? FALSE : TRUE);
1495
1496 }
1497
1498 int
1499 KFW_AFS_renew_tokens_for_all_cells(void)
1500 {
1501     struct cell_principal_map * next = cell_princ_map;
1502
1503     if ( IsDebuggerPresent() )
1504         OutputDebugString("KFW_AFS_renew_tokens_for_all()\n");
1505
1506     if ( !next )
1507         return 0;
1508
1509     for ( ; next ; next = next->next ) {
1510         if ( next->active )
1511             KFW_AFS_renew_token_for_cell(next->cell);
1512     }
1513     return 0;
1514 }
1515
1516 int
1517 KFW_renew(krb5_context alt_ctx, krb5_ccache alt_cc)
1518 {
1519     krb5_error_code                     code = 0;
1520     krb5_context                        ctx = 0;
1521     krb5_ccache                         cc = 0;
1522     krb5_principal                      me = 0;
1523     krb5_principal              server = 0;
1524     krb5_creds                          my_creds;
1525     krb5_data                   *realm = 0;
1526
1527         memset(&my_creds, 0, sizeof(krb5_creds));
1528
1529     if ( alt_ctx ) {
1530         ctx = alt_ctx;
1531     } else {
1532         code = pkrb5_init_context(&ctx);
1533         if (code) goto cleanup;
1534     }
1535
1536     if ( alt_cc ) {
1537         cc = alt_cc;
1538     } else {
1539         code = pkrb5_cc_default(ctx, &cc);
1540         if (code) goto cleanup;
1541     }
1542
1543     code = pkrb5_cc_get_principal(ctx, cc, &me);
1544     if (code) goto cleanup;
1545
1546     realm = krb5_princ_realm(ctx, me);
1547
1548     code = pkrb5_build_principal_ext(ctx, &server,
1549                                     realm->length,realm->data,
1550                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
1551                                     realm->length,realm->data,
1552                                     0);
1553     if ( code ) 
1554         goto cleanup;
1555
1556     if ( IsDebuggerPresent() ) {
1557         char * cname, *sname;
1558         pkrb5_unparse_name(ctx, me, &cname);
1559         pkrb5_unparse_name(ctx, server, &sname);
1560         OutputDebugString("Renewing credential for client \"");
1561         OutputDebugString(cname);
1562         OutputDebugString("\" and service \"");
1563         OutputDebugString(sname);
1564         OutputDebugString("\"\n");
1565         pkrb5_free_unparsed_name(ctx,cname);
1566         pkrb5_free_unparsed_name(ctx,sname);
1567     }
1568
1569     my_creds.client = me;
1570     my_creds.server = server;
1571
1572     code = pkrb5_get_renewed_creds(ctx, &my_creds, me, cc, NULL);
1573     if (code) {
1574         if ( IsDebuggerPresent() ) {
1575             char message[256];
1576             sprintf(message,"krb5_get_renewed_creds() failed: %d\n",code);
1577             OutputDebugString(message);
1578         }
1579         goto cleanup;
1580     }
1581
1582     code = pkrb5_cc_initialize(ctx, cc, me);
1583     if (code) {
1584         if ( IsDebuggerPresent() ) {
1585             char message[256];
1586             sprintf(message,"krb5_cc_initialize() failed: %d\n",code);
1587             OutputDebugString(message);
1588         }
1589         goto cleanup;
1590     }
1591
1592     code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
1593     if (code) {
1594         if ( IsDebuggerPresent() ) {
1595             char message[256];
1596             sprintf(message,"krb5_cc_store_cred() failed: %d\n",code);
1597             OutputDebugString(message);
1598         }
1599         goto cleanup;
1600     }
1601
1602   cleanup:
1603     if (my_creds.client == me)
1604         my_creds.client = 0;
1605     if (my_creds.server == server)
1606         my_creds.server = 0;
1607     pkrb5_free_cred_contents(ctx, &my_creds);
1608     if (me)
1609         pkrb5_free_principal(ctx, me);
1610     if (server)
1611         pkrb5_free_principal(ctx, server);
1612     if (cc && (cc != alt_cc))
1613         pkrb5_cc_close(ctx, cc);
1614     if (ctx && (ctx != alt_ctx))
1615         pkrb5_free_context(ctx);
1616     return(code);
1617 }
1618
1619 int
1620 KFW_kinit( krb5_context alt_ctx,
1621             krb5_ccache  alt_cc,
1622             HWND hParent,
1623             char *principal_name,
1624             char *password,
1625             krb5_deltat lifetime,
1626             DWORD                       forwardable,
1627             DWORD                       proxiable,
1628             krb5_deltat                 renew_life,
1629             DWORD                       addressless,
1630             DWORD                       publicIP
1631             )
1632 {
1633     krb5_error_code                     code = 0;
1634     krb5_context                        ctx = 0;
1635     krb5_ccache                         cc = 0;
1636     krb5_principal                      me = 0;
1637     char*                       name = 0;
1638     krb5_creds                          my_creds;
1639     krb5_get_init_creds_opt     options;
1640     krb5_address **             addrs = NULL;
1641     int                         i = 0, addr_count = 0;
1642
1643     if (!pkrb5_init_context)
1644         return 0;
1645
1646     pkrb5_get_init_creds_opt_init(&options);
1647     memset(&my_creds, 0, sizeof(my_creds));
1648
1649     if (alt_ctx)
1650     {
1651         ctx = alt_ctx;
1652     }
1653     else
1654     {
1655         code = pkrb5_init_context(&ctx);
1656         if (code) goto cleanup;
1657     }
1658
1659     if ( alt_cc ) {
1660         cc = alt_cc;
1661     } else {
1662         code = pkrb5_cc_default(ctx, &cc);  
1663         if (code) goto cleanup;
1664     }
1665
1666     code = pkrb5_parse_name(ctx, principal_name, &me);
1667     if (code) 
1668                 goto cleanup;
1669
1670     code = pkrb5_unparse_name(ctx, me, &name);
1671     if (code) 
1672                 goto cleanup;
1673
1674     if (lifetime == 0)
1675         lifetime = pLeash_get_default_lifetime();
1676     else
1677         lifetime *= 5*60;
1678
1679         if (renew_life > 0)
1680                 renew_life *= 5*60;
1681
1682     if (lifetime)
1683         pkrb5_get_init_creds_opt_set_tkt_life(&options, lifetime);
1684         pkrb5_get_init_creds_opt_set_forwardable(&options,
1685                                                  forwardable ? 1 : 0);
1686         pkrb5_get_init_creds_opt_set_proxiable(&options,
1687                                                proxiable ? 1 : 0);
1688         pkrb5_get_init_creds_opt_set_renew_life(&options,
1689                                                renew_life);
1690     if (addressless)
1691         pkrb5_get_init_creds_opt_set_address_list(&options,NULL);
1692     else {
1693                 if (publicIP)
1694         {
1695             // we are going to add the public IP address specified by the user
1696             // to the list provided by the operating system
1697             krb5_address ** local_addrs=NULL;
1698             DWORD           netIPAddr;
1699
1700             pkrb5_os_localaddr(ctx, &local_addrs);
1701             while ( local_addrs[i++] );
1702             addr_count = i + 1;
1703
1704             addrs = (krb5_address **) malloc((addr_count+1) * sizeof(krb5_address *));
1705             if ( !addrs ) {
1706                 pkrb5_free_addresses(ctx, local_addrs);
1707                 goto cleanup;
1708             }
1709             memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1));
1710             i = 0;
1711             while ( local_addrs[i] ) {
1712                 addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
1713                 if (addrs[i] == NULL) {
1714                     pkrb5_free_addresses(ctx, local_addrs);
1715                     goto cleanup;
1716                 }
1717
1718                 addrs[i]->magic = local_addrs[i]->magic;
1719                 addrs[i]->addrtype = local_addrs[i]->addrtype;
1720                 addrs[i]->length = local_addrs[i]->length;
1721                 addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
1722                 if (!addrs[i]->contents) {
1723                     pkrb5_free_addresses(ctx, local_addrs);
1724                     goto cleanup;
1725                 }
1726
1727                 memcpy(addrs[i]->contents,local_addrs[i]->contents,
1728                         local_addrs[i]->length);        /* safe */
1729                 i++;
1730             }
1731             pkrb5_free_addresses(ctx, local_addrs);
1732
1733             addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
1734             if (addrs[i] == NULL)
1735                 goto cleanup;
1736
1737             addrs[i]->magic = KV5M_ADDRESS;
1738             addrs[i]->addrtype = AF_INET;
1739             addrs[i]->length = 4;
1740             addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
1741             if (!addrs[i]->contents)
1742                 goto cleanup;
1743
1744             netIPAddr = htonl(publicIP);
1745             memcpy(addrs[i]->contents,&netIPAddr,4);
1746         
1747             pkrb5_get_init_creds_opt_set_address_list(&options,addrs);
1748
1749         }
1750     }
1751
1752     code = pkrb5_get_init_creds_password(ctx, 
1753                                        &my_creds, 
1754                                        me,
1755                                        password, // password
1756                                        KRB5_prompter, // prompter
1757                                        hParent, // prompter data
1758                                        0, // start time
1759                                        0, // service name
1760                                        &options);
1761     if (code) 
1762                 goto cleanup;
1763
1764     code = pkrb5_cc_initialize(ctx, cc, me);
1765     if (code) 
1766                 goto cleanup;
1767
1768     code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
1769     if (code) 
1770                 goto cleanup;
1771
1772  cleanup:
1773     if ( addrs ) {
1774         for ( i=0;i<addr_count;i++ ) {
1775             if ( addrs[i] ) {
1776                 if ( addrs[i]->contents )
1777                     free(addrs[i]->contents);
1778                 free(addrs[i]);
1779             }
1780         }
1781     }
1782     if (my_creds.client == me)
1783         my_creds.client = 0;
1784     pkrb5_free_cred_contents(ctx, &my_creds);
1785     if (name)
1786         pkrb5_free_unparsed_name(ctx, name);
1787     if (me)
1788         pkrb5_free_principal(ctx, me);
1789     if (cc && (cc != alt_cc))
1790         pkrb5_cc_close(ctx, cc);
1791     if (ctx && (ctx != alt_ctx))
1792         pkrb5_free_context(ctx);
1793     return(code);
1794 }
1795
1796
1797 int
1798 KFW_kdestroy(krb5_context alt_ctx, krb5_ccache alt_cc)
1799 {
1800     krb5_context                ctx;
1801     krb5_ccache                 cc;
1802     krb5_error_code             code;
1803
1804     if (alt_ctx)
1805     {
1806         ctx = alt_ctx;
1807     }
1808     else
1809     {
1810         code = pkrb5_init_context(&ctx);
1811         if (code) goto cleanup;
1812     }
1813
1814     if ( alt_cc ) {
1815         cc = alt_cc;
1816     } else {
1817         code = pkrb5_cc_default(ctx, &cc);  
1818         if (code) goto cleanup;
1819     }
1820
1821     code = pkrb5_cc_destroy(ctx, cc);
1822     if ( !code ) cc = 0;
1823
1824   cleanup:
1825     if (cc && (cc != alt_cc))
1826         pkrb5_cc_close(ctx, cc);
1827     if (ctx && (ctx != alt_ctx))
1828         pkrb5_free_context(ctx);
1829
1830     return(code);
1831 }
1832
1833
1834 #ifdef USE_MS2MIT
1835 static BOOL
1836 GetSecurityLogonSessionData(PSECURITY_LOGON_SESSION_DATA * ppSessionData)
1837 {
1838     NTSTATUS Status = 0;
1839     HANDLE  TokenHandle;
1840     TOKEN_STATISTICS Stats;
1841     DWORD   ReqLen;
1842     BOOL    Success;
1843
1844     if (!ppSessionData)
1845         return FALSE;
1846     *ppSessionData = NULL;
1847
1848     Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle );
1849     if ( !Success )
1850         return FALSE;
1851
1852     Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );
1853     CloseHandle( TokenHandle );
1854     if ( !Success )
1855         return FALSE;
1856
1857     Status = pLsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );
1858     if ( FAILED(Status) || !ppSessionData )
1859         return FALSE;
1860
1861     return TRUE;
1862 }
1863
1864 //
1865 // MSLSA_IsKerberosLogon() does not validate whether or not there are valid tickets in the 
1866 // cache.  It validates whether or not it is reasonable to assume that if we 
1867 // attempted to retrieve valid tickets we could do so.  Microsoft does not 
1868 // automatically renew expired tickets.  Therefore, the cache could contain
1869 // expired or invalid tickets.  Microsoft also caches the user's password 
1870 // and will use it to retrieve new TGTs if the cache is empty and tickets
1871 // are requested.
1872
1873 static BOOL
1874 MSLSA_IsKerberosLogon(VOID)
1875 {
1876     PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
1877     BOOL    Success = FALSE;
1878
1879     if ( GetSecurityLogonSessionData(&pSessionData) ) {
1880         if ( pSessionData->AuthenticationPackage.Buffer ) {
1881             WCHAR buffer[256];
1882             WCHAR *usBuffer;
1883             int usLength;
1884
1885             Success = FALSE;
1886             usBuffer = (pSessionData->AuthenticationPackage).Buffer;
1887             usLength = (pSessionData->AuthenticationPackage).Length;
1888             if (usLength < 256)
1889             {
1890                 lstrcpynW (buffer, usBuffer, usLength);
1891                 lstrcatW (buffer,L"");
1892                 if ( !lstrcmpW(L"Kerberos",buffer) )
1893                     Success = TRUE;
1894             }
1895         }
1896         pLsaFreeReturnBuffer(pSessionData);
1897     }
1898     return Success;
1899 }
1900 #endif /* USE_MS2MIT */
1901
1902 static BOOL CALLBACK 
1903 MultiInputDialogProc( HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam)
1904 {
1905     int i;
1906
1907     switch ( message ) {
1908     case WM_INITDIALOG:
1909         if ( GetDlgCtrlID((HWND) wParam) != ID_MID_TEXT )
1910         {
1911             SetFocus(GetDlgItem( hDialog, ID_MID_TEXT));
1912             return FALSE;
1913         }
1914                 for ( i=0; i < mid_cnt ; i++ ) {
1915                         if (mid_tb[i].echo == 0)
1916                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, 32, 0);
1917                     else if (mid_tb[i].echo == 2) 
1918                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, '*', 0);
1919                 }
1920         return TRUE;
1921
1922     case WM_COMMAND:
1923         switch ( LOWORD(wParam) ) {
1924         case IDOK:
1925             for ( i=0; i < mid_cnt ; i++ ) {
1926                 if ( !GetDlgItemText(hDialog, ID_MID_TEXT+i, mid_tb[i].buf, mid_tb[i].len) )
1927                     *mid_tb[i].buf = '\0';
1928             }
1929             /* fallthrough */
1930         case IDCANCEL:
1931             EndDialog(hDialog, LOWORD(wParam));
1932             return TRUE;
1933         }
1934     }
1935     return FALSE;
1936 }
1937
1938 static LPWORD 
1939 lpwAlign( LPWORD lpIn )
1940 {
1941     ULONG ul;
1942
1943     ul = (ULONG) lpIn;
1944     ul += 3;
1945     ul >>=2;
1946     ul <<=2;
1947     return (LPWORD) ul;;
1948 }
1949
1950 /*
1951  * dialog widths are measured in 1/4 character widths
1952  * dialog height are measured in 1/8 character heights
1953  */
1954
1955 static LRESULT
1956 MultiInputDialog( HINSTANCE hinst, HWND hwndOwner, 
1957                   char * ptext[], int numlines, int width, 
1958                   int tb_cnt, struct textField * tb)
1959 {
1960     HGLOBAL hgbl;
1961     LPDLGTEMPLATE lpdt;
1962     LPDLGITEMTEMPLATE lpdit;
1963     LPWORD lpw;
1964     LPWSTR lpwsz;
1965     LRESULT ret;
1966     int nchar, i, pwid;
1967
1968     hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
1969     if (!hgbl)
1970         return -1;
1971  
1972     mid_cnt = tb_cnt;
1973     mid_tb = tb;
1974
1975     lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
1976  
1977     // Define a dialog box.
1978  
1979     lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU
1980                    | DS_MODALFRAME | WS_CAPTION | DS_CENTER 
1981                    | DS_SETFOREGROUND | DS_3DLOOK
1982                    | DS_SETFONT | DS_FIXEDSYS | DS_NOFAILCREATE;
1983     lpdt->cdit = numlines + (2 * tb_cnt) + 2;  // number of controls
1984     lpdt->x  = 10;  
1985     lpdt->y  = 10;
1986     lpdt->cx = 20 + width * 4; 
1987     lpdt->cy = 20 + (numlines + tb_cnt + 4) * 14;
1988
1989     lpw = (LPWORD) (lpdt + 1);
1990     *lpw++ = 0;   // no menu
1991     *lpw++ = 0;   // predefined dialog box class (by default)
1992
1993     lpwsz = (LPWSTR) lpw;
1994     nchar = MultiByteToWideChar (CP_ACP, 0, "", -1, lpwsz, 128);
1995     lpw   += nchar;
1996     *lpw++ = 8;                        // font size (points)
1997     lpwsz = (LPWSTR) lpw;
1998     nchar = MultiByteToWideChar (CP_ACP, 0, "MS Shell Dlg", 
1999                                     -1, lpwsz, 128);
2000     lpw   += nchar;
2001
2002     //-----------------------
2003     // Define an OK button.
2004     //-----------------------
2005     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
2006     lpdit = (LPDLGITEMTEMPLATE) lpw;
2007     lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP | WS_BORDER;
2008     lpdit->dwExtendedStyle = 0;
2009     lpdit->x  = (lpdt->cx - 14)/4 - 20; 
2010     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
2011     lpdit->cx = 40; 
2012     lpdit->cy = 14;
2013     lpdit->id = IDOK;  // OK button identifier
2014
2015     lpw = (LPWORD) (lpdit + 1);
2016     *lpw++ = 0xFFFF;
2017     *lpw++ = 0x0080;    // button class
2018
2019     lpwsz = (LPWSTR) lpw;
2020     nchar = MultiByteToWideChar (CP_ACP, 0, "OK", -1, lpwsz, 50);
2021     lpw   += nchar;
2022     *lpw++ = 0;           // no creation data
2023
2024     //-----------------------
2025     // Define an Cancel button.
2026     //-----------------------
2027     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
2028     lpdit = (LPDLGITEMTEMPLATE) lpw;
2029     lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP | WS_BORDER;
2030     lpdit->dwExtendedStyle = 0;
2031     lpdit->x  = (lpdt->cx - 14)*3/4 - 20; 
2032     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
2033     lpdit->cx = 40; 
2034     lpdit->cy = 14;
2035     lpdit->id = IDCANCEL;  // CANCEL button identifier
2036
2037     lpw = (LPWORD) (lpdit + 1);
2038     *lpw++ = 0xFFFF;
2039     *lpw++ = 0x0080;    // button class
2040
2041     lpwsz = (LPWSTR) lpw;
2042     nchar = MultiByteToWideChar (CP_ACP, 0, "Cancel", -1, lpwsz, 50);
2043     lpw   += nchar;
2044     *lpw++ = 0;           // no creation data
2045
2046     /* Add controls for preface data */
2047     for ( i=0; i<numlines; i++) {
2048         /*-----------------------
2049          * Define a static text control.
2050          *-----------------------*/
2051         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
2052         lpdit = (LPDLGITEMTEMPLATE) lpw;
2053         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
2054         lpdit->dwExtendedStyle = 0;
2055         lpdit->x  = 10; 
2056         lpdit->y  = 10 + i * 14;
2057         lpdit->cx = strlen(ptext[i]) * 4 + 10; 
2058         lpdit->cy = 14;
2059         lpdit->id = ID_TEXT + i;  // text identifier
2060
2061         lpw = (LPWORD) (lpdit + 1);
2062         *lpw++ = 0xFFFF;
2063         *lpw++ = 0x0082;                         // static class
2064
2065         lpwsz = (LPWSTR) lpw;
2066         nchar = MultiByteToWideChar (CP_ACP, 0, ptext[i], 
2067                                          -1, lpwsz, 2*width);
2068         lpw   += nchar;
2069         *lpw++ = 0;           // no creation data
2070     }
2071     
2072     for ( i=0, pwid = 0; i<tb_cnt; i++) {
2073         if ( pwid < strlen(tb[i].label) )
2074             pwid = strlen(tb[i].label);
2075     }
2076
2077     for ( i=0; i<tb_cnt; i++) {
2078         /* Prompt */
2079         /*-----------------------
2080          * Define a static text control.
2081          *-----------------------*/
2082         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
2083         lpdit = (LPDLGITEMTEMPLATE) lpw;
2084         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
2085         lpdit->dwExtendedStyle = 0;
2086         lpdit->x  = 10; 
2087         lpdit->y  = 10 + (numlines + i + 1) * 14;
2088         lpdit->cx = pwid * 4; 
2089         lpdit->cy = 14;
2090         lpdit->id = ID_TEXT + numlines + i;  // text identifier
2091
2092         lpw = (LPWORD) (lpdit + 1);
2093         *lpw++ = 0xFFFF;
2094         *lpw++ = 0x0082;                         // static class
2095
2096         lpwsz = (LPWSTR) lpw;
2097         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].label ? tb[i].label : "", 
2098                                      -1, lpwsz, 128);
2099         lpw   += nchar;
2100         *lpw++ = 0;           // no creation data
2101
2102         /*-----------------------
2103          * Define an edit control.
2104          *-----------------------*/
2105         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
2106         lpdit = (LPDLGITEMTEMPLATE) lpw;
2107         lpdit->style = WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | WS_BORDER | (tb[i].echo == 1 ? 0L : ES_PASSWORD);
2108         lpdit->dwExtendedStyle = 0;
2109         lpdit->x  = 10 + (pwid + 1) * 4; 
2110         lpdit->y  = 10 + (numlines + i + 1) * 14;
2111         lpdit->cx = (width - (pwid + 1)) * 4; 
2112         lpdit->cy = 14;
2113         lpdit->id = ID_MID_TEXT + i;             // identifier
2114
2115         lpw = (LPWORD) (lpdit + 1);
2116         *lpw++ = 0xFFFF;
2117         *lpw++ = 0x0081;                         // edit class
2118
2119         lpwsz = (LPWSTR) lpw;
2120         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].def ? tb[i].def : "", 
2121                                      -1, lpwsz, 128);
2122         lpw   += nchar;
2123         *lpw++ = 0;           // no creation data
2124     }
2125
2126     GlobalUnlock(hgbl); 
2127     ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl, 
2128                                                         hwndOwner, (DLGPROC) MultiInputDialogProc); 
2129     GlobalFree(hgbl); 
2130
2131     switch ( ret ) {
2132     case 0:     /* Timeout */
2133         return -1;
2134     case IDOK:
2135         return 1;
2136     case IDCANCEL:
2137         return 0;
2138     default: {
2139         char buf[256];
2140         sprintf(buf,"DialogBoxIndirect() failed: %d",GetLastError());
2141         MessageBox(hwndOwner,
2142                     buf,
2143                     "GetLastError()",
2144                     MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
2145         return -1;
2146     }
2147     }
2148 }
2149
2150 static int
2151 multi_field_dialog(HWND hParent, char * preface, int n, struct textField tb[])
2152 {
2153         HINSTANCE hInst = 0;
2154     int maxwidth = 0;
2155     int numlines = 0;
2156     int len;
2157     char * plines[16], *p = preface ? preface : "";
2158     int i;
2159
2160     for ( i=0; i<16; i++ ) 
2161         plines[i] = NULL;
2162
2163     while (*p && numlines < 16) {
2164         plines[numlines++] = p;
2165         for ( ;*p && *p != '\r' && *p != '\n'; p++ );
2166         if ( *p == '\r' && *(p+1) == '\n' ) {
2167             *p++ = '\0';
2168             p++;
2169         } else if ( *p == '\n' ) {
2170             *p++ = '\0';
2171         } 
2172         if ( strlen(plines[numlines-1]) > maxwidth )
2173             maxwidth = strlen(plines[numlines-1]);
2174     }
2175
2176     for ( i=0;i<n;i++ ) {
2177         len = strlen(tb[i].label) + 1 + (tb[i].len > 40 ? 40 : tb[i].len);
2178         if ( maxwidth < len )
2179             maxwidth = len;
2180     }
2181
2182     return(MultiInputDialog(hInst, hParent, plines, numlines, maxwidth, n, tb));
2183 }
2184
2185 static krb5_error_code KRB5_CALLCONV
2186 KRB5_prompter( krb5_context context,
2187                void *data,
2188                const char *name,
2189                const char *banner,
2190                int num_prompts,
2191                krb5_prompt prompts[])
2192 {
2193     krb5_error_code     errcode = 0;
2194     int                 i;
2195     struct textField * tb = NULL;
2196     int    len = 0, blen=0, nlen=0;
2197         HWND hParent = (HWND)data;
2198
2199     if (name)
2200         nlen = strlen(name)+2;
2201
2202     if (banner)
2203         blen = strlen(banner)+2;
2204
2205     tb = (struct textField *) malloc(sizeof(struct textField) * num_prompts);
2206     if ( tb != NULL ) {
2207         int ok;
2208         memset(tb,0,sizeof(struct textField) * num_prompts);
2209         for ( i=0; i < num_prompts; i++ ) {
2210             tb[i].buf = prompts[i].reply->data;
2211             tb[i].len = prompts[i].reply->length;
2212             tb[i].label = prompts[i].prompt;
2213             tb[i].def = NULL;
2214             tb[i].echo = (prompts[i].hidden ? 2 : 1);
2215         }   
2216
2217         ok = multi_field_dialog(hParent,(char *)banner,num_prompts,tb);
2218         if ( ok ) {
2219             for ( i=0; i < num_prompts; i++ )
2220                 prompts[i].reply->length = strlen(prompts[i].reply->data);
2221         } else
2222             errcode = -2;
2223     }
2224
2225     if ( tb )
2226         free(tb);
2227     if (errcode) {
2228         for (i = 0; i < num_prompts; i++) {
2229             memset(prompts[i].reply->data, 0, prompts[i].reply->length);
2230         }
2231     }
2232     return errcode;
2233 }
2234
2235 BOOL
2236 KFW_AFS_wait_for_service_start(void)
2237 {
2238     char    HostName[64];
2239     DWORD   CurrentState;
2240
2241     CurrentState = SERVICE_START_PENDING;
2242     memset(HostName, '\0', sizeof(HostName));
2243     gethostname(HostName, sizeof(HostName));
2244
2245     while (CurrentState != SERVICE_RUNNING || CurrentState != SERVICE_STOPPED)
2246     {
2247         if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
2248             return(0);
2249         if ( IsDebuggerPresent() ) {
2250             switch ( CurrentState ) {
2251             case SERVICE_STOPPED:
2252                 OutputDebugString("SERVICE_STOPPED\n");
2253                 break;
2254             case SERVICE_START_PENDING:
2255                 OutputDebugString("SERVICE_START_PENDING\n");
2256                 break;
2257             case SERVICE_STOP_PENDING:
2258                 OutputDebugString("SERVICE_STOP_PENDING\n");
2259                 break;
2260             case SERVICE_RUNNING:
2261                 OutputDebugString("SERVICE_RUNNING\n");
2262                 break;
2263             case SERVICE_CONTINUE_PENDING:
2264                 OutputDebugString("SERVICE_CONTINUE_PENDING\n");
2265                 break;
2266             case SERVICE_PAUSE_PENDING:
2267                 OutputDebugString("SERVICE_PAUSE_PENDING\n");
2268                 break;
2269             case SERVICE_PAUSED:
2270                 OutputDebugString("SERVICE_PAUSED\n");
2271                 break;
2272             default:
2273                 OutputDebugString("UNKNOWN Service State\n");
2274             }
2275         }
2276         if (CurrentState == SERVICE_STOPPED)
2277             return(0);
2278         if (CurrentState == SERVICE_RUNNING)
2279             return(1);
2280         Sleep(500);
2281     }
2282     return(0);
2283 }
2284
2285
2286 int
2287 KFW_AFS_unlog(void)
2288 {
2289     long        rc;
2290     char    HostName[64];
2291     DWORD   CurrentState;
2292
2293     CurrentState = 0;
2294     memset(HostName, '\0', sizeof(HostName));
2295     gethostname(HostName, sizeof(HostName));
2296     if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
2297         return(0);
2298     if (CurrentState != SERVICE_RUNNING)
2299         return(0);
2300
2301     rc = pktc_ForgetAllTokens();
2302
2303     return(0);
2304 }
2305
2306 int
2307 KFW_AFS_klog(
2308     krb5_context alt_ctx,
2309     krb5_ccache  alt_cc,
2310     char *service,
2311     char *cell,
2312     char *realm,
2313     int LifeTime
2314     )
2315 {
2316     long        rc = 0;
2317     CREDENTIALS creds;
2318     KTEXT_ST    ticket;
2319     struct ktc_principal        aserver;
2320     struct ktc_principal        aclient;
2321     char        username[BUFSIZ];       /* To hold client username structure */
2322     char        realm_of_user[REALM_SZ]; /* Kerberos realm of user */
2323     char        realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
2324     char        local_cell[MAXCELLCHARS+1];
2325     char        Dmycell[MAXCELLCHARS+1];
2326     struct ktc_token    atoken;
2327     struct ktc_token    btoken;
2328     afsconf_cell        ak_cellconfig; /* General information about the cell */
2329     char        RealmName[128];
2330     char        CellName[128];
2331     char        ServiceName[128];
2332     DWORD       CurrentState;
2333     char        HostName[64];
2334     BOOL        try_krb5 = 0;
2335     krb5_context  ctx = 0;
2336     krb5_ccache   cc = 0;
2337     krb5_creds increds;
2338     krb5_creds * k5creds = 0;
2339     krb5_error_code code;
2340     krb5_principal client_principal = 0;
2341     int i, retry = 0;
2342
2343     CurrentState = 0;
2344     memset(HostName, '\0', sizeof(HostName));
2345     gethostname(HostName, sizeof(HostName));
2346     if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR) {
2347         if ( IsDebuggerPresent() )
2348             OutputDebugString("Unable to retrieve AFSD Service Status\n");
2349         return(-1);
2350     }
2351     if (CurrentState != SERVICE_RUNNING) {
2352         if ( IsDebuggerPresent() )
2353             OutputDebugString("AFSD Service NOT RUNNING\n");
2354         return(-2);
2355     }
2356
2357     memset(RealmName, '\0', sizeof(RealmName));
2358     memset(CellName, '\0', sizeof(CellName));
2359     memset(ServiceName, '\0', sizeof(ServiceName));
2360     memset(realm_of_user, '\0', sizeof(realm_of_user));
2361     memset(realm_of_cell, '\0', sizeof(realm_of_cell));
2362         if (cell && cell[0])
2363                 strcpy(Dmycell, cell);
2364         else
2365                 memset(Dmycell, '\0', sizeof(Dmycell));
2366
2367     // NULL or empty cell returns information on local cell
2368     if (rc = get_cellconfig(Dmycell, &ak_cellconfig, local_cell))
2369     {
2370                 KFW_AFS_error(rc, "get_cellconfig()");
2371         return(rc);
2372     }
2373
2374     if ( alt_ctx ) {
2375         ctx = alt_ctx;
2376     } else {
2377         code = pkrb5_init_context(&ctx);
2378         if (code) goto cleanup;
2379     }
2380
2381     if ( alt_cc ) {
2382         cc = alt_cc;
2383     } else {
2384         code = pkrb5_cc_default(ctx, &cc);
2385         if (code) goto skip_krb5_init;
2386     }
2387
2388     memset((char *)&increds, 0, sizeof(increds));
2389
2390     code = pkrb5_cc_get_principal(ctx, cc, &client_principal);
2391         if (code) {
2392         if ( code == KRB5_CC_NOTFOUND && IsDebuggerPresent() ) 
2393         {
2394             OutputDebugString("Principal Not Found for ccache\n");
2395         }
2396         goto skip_krb5_init;
2397     }
2398     i = krb5_princ_realm(ctx, client_principal)->length;
2399     if (i > REALM_SZ-1) 
2400         i = REALM_SZ-1;
2401     strncpy(realm_of_user,krb5_princ_realm(ctx, client_principal)->data,i);
2402     realm_of_user[i] = 0;
2403     try_krb5 = 1;
2404
2405   skip_krb5_init:
2406 #ifdef USE_KRB4
2407     if ( !try_krb5 || !realm_of_user[0] ) {
2408         if ((rc = (*pkrb_get_tf_realm)((*ptkt_string)(), realm_of_user)) != KSUCCESS)
2409         {
2410             goto cleanup;
2411         }       
2412     }
2413 #else
2414     goto cleanup;
2415 #endif
2416     strcpy(realm_of_cell, afs_realm_of_cell(&ak_cellconfig));
2417
2418     if (strlen(service) == 0)
2419         strcpy(ServiceName, "afs");
2420     else
2421         strcpy(ServiceName, service);
2422
2423     if (strlen(cell) == 0)
2424         strcpy(CellName, local_cell);
2425     else
2426         strcpy(CellName, cell);
2427
2428     if (strlen(realm) == 0)
2429         strcpy(RealmName, realm_of_cell);
2430     else
2431         strcpy(RealmName, realm);
2432
2433     memset(&creds, '\0', sizeof(creds));
2434
2435     if ( try_krb5 ) {
2436         /* First try service/cell@REALM */
2437         if (code = pkrb5_build_principal(ctx, &increds.server,
2438                                           strlen(RealmName),
2439                                           RealmName,
2440                                           ServiceName,
2441                                           CellName,
2442                                           0)) 
2443         {
2444             goto cleanup;
2445         }
2446
2447         increds.client = client_principal;
2448         increds.times.endtime = 0;
2449         /* Ask for DES since that is what V4 understands */
2450         increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
2451
2452         if ( IsDebuggerPresent() ) {
2453             char * cname, *sname;
2454             pkrb5_unparse_name(ctx, increds.client, &cname);
2455             pkrb5_unparse_name(ctx, increds.server, &sname);
2456             OutputDebugString("Getting credentials for \"");
2457             OutputDebugString(cname);
2458             OutputDebugString("\" and service \"");
2459             OutputDebugString(sname);
2460             OutputDebugString("\"\n");
2461             pkrb5_free_unparsed_name(ctx,cname);
2462             pkrb5_free_unparsed_name(ctx,sname);
2463         }
2464
2465         code = pkrb5_get_credentials(ctx, 0, cc, &increds, &k5creds);
2466         if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
2467              code == KRB5KRB_ERR_GENERIC /* heimdal */) {
2468             /* Or service@REALM */
2469             pkrb5_free_principal(ctx,increds.server);
2470             increds.server = 0;
2471             code = pkrb5_build_principal(ctx, &increds.server,
2472                                           strlen(RealmName),
2473                                           RealmName,
2474                                           ServiceName,
2475                                           0);
2476
2477             if ( IsDebuggerPresent() ) {
2478                 char * cname, *sname;
2479                 pkrb5_unparse_name(ctx, increds.client, &cname);
2480                 pkrb5_unparse_name(ctx, increds.server, &sname);
2481                 OutputDebugString("krb5_get_credentials() returned Service Principal Unknown\n");
2482                 OutputDebugString("Trying again: getting credentials for \"");
2483                 OutputDebugString(cname);
2484                 OutputDebugString("\" and service \"");
2485                 OutputDebugString(sname);
2486                 OutputDebugString("\"\n");
2487                 pkrb5_free_unparsed_name(ctx,cname);
2488                 pkrb5_free_unparsed_name(ctx,sname);
2489             }
2490
2491             if (!code)
2492                 code = pkrb5_get_credentials(ctx, 0, cc, &increds, &k5creds);
2493         }
2494
2495         if (code) {
2496             if ( IsDebuggerPresent() ) {
2497                 char message[256];
2498                 sprintf(message,"krb5_get_credentials returns: %d\n",code);
2499                 OutputDebugString(message);
2500             }
2501             try_krb5 = 0;
2502             goto use_krb4;
2503         }
2504         /* This requires krb524d to be running with the KDC */
2505         code = pkrb524_convert_creds_kdc(ctx, k5creds, &creds);
2506         pkrb5_free_creds(ctx, k5creds);
2507         if (code) {
2508             if ( IsDebuggerPresent() ) {
2509                 char message[256];
2510                 sprintf(message,"krb524_convert_creds_kdc returns: %d\n",code);
2511                 OutputDebugString(message);
2512             }
2513             try_krb5 = 0;
2514             goto use_krb4;
2515         }
2516     } else {
2517       use_krb4:
2518 #ifdef USE_KRB4
2519         rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds);
2520         if (rc == NO_TKT_FIL) {
2521             // if the problem is that we have no krb4 tickets
2522             // do not attempt to continue
2523             goto cleanup;
2524         }
2525         if (rc != KSUCCESS)
2526             rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds);
2527
2528         if (rc != KSUCCESS)
2529         {
2530             if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, CellName, RealmName, 0)) == KSUCCESS)
2531             {
2532                 if ((rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds)) != KSUCCESS)
2533                 {
2534                     goto cleanup;
2535                 }
2536             }
2537             else if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, "", RealmName, 0)) == KSUCCESS)
2538             {
2539                 if ((rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds)) != KSUCCESS)
2540                 {
2541                     goto cleanup;
2542                 }
2543             }
2544             else
2545             {
2546                 goto cleanup;
2547             }
2548         }
2549 #else
2550         goto cleanup;
2551 #endif
2552     }
2553
2554     memset(&aserver, '\0', sizeof(aserver));
2555     strncpy(aserver.name, ServiceName, MAXKTCNAMELEN - 1);
2556     strncpy(aserver.cell, CellName, MAXKTCREALMLEN - 1);
2557
2558     strcpy(username, creds.pname);
2559     if (creds.pinst[0]) 
2560     {
2561         strcat(username, ".");
2562         strcat(username, creds.pinst);
2563     }
2564
2565     memset(&atoken, '\0', sizeof(atoken));
2566     atoken.kvno = creds.kvno;
2567     atoken.startTime = creds.issue_date;
2568     atoken.endTime = creds.issue_date + (creds.lifetime * 300);
2569     memcpy(&atoken.sessionKey, creds.session, 8);
2570     atoken.ticketLen = creds.ticket_st.length;
2571     memcpy(atoken.ticket, creds.ticket_st.dat, atoken.ticketLen);
2572
2573   retry_gettoken:
2574     rc = pktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient);
2575     if (rc != 0 && rc != KTC_NOENT && rc != KTC_NOCELL) {
2576         if ( rc == KTC_NOCM && retry < 20 ) {
2577             Sleep(500);
2578             retry++;
2579             goto retry_gettoken;
2580         }
2581         KFW_AFS_error(rc, "ktc_GetToken()");
2582         code = rc;
2583         goto cleanup;
2584     }
2585
2586     if (atoken.kvno == btoken.kvno &&
2587         atoken.ticketLen == btoken.ticketLen &&
2588         !memcmp(&atoken.sessionKey, &btoken.sessionKey, sizeof(atoken.sessionKey)) &&
2589         !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) 
2590     {
2591         goto cleanup;
2592     }
2593
2594     // * Reset the "aclient" structure before we call ktc_SetToken.
2595     // * This structure was first set by the ktc_GetToken call when
2596     // * we were comparing whether identical tokens already existed.
2597
2598     strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
2599     strcpy(aclient.instance, "");
2600     strncpy(aclient.cell, creds.realm, MAXKTCREALMLEN - 1);
2601
2602     if (rc = pktc_SetToken(&aserver, &atoken, &aclient, 0))
2603     {
2604         KFW_AFS_error(rc, "ktc_SetToken()");
2605         code = rc;
2606         goto cleanup;
2607     }
2608
2609   cleanup:
2610     if (client_principal)
2611         pkrb5_free_principal(ctx,client_principal);
2612     /* increds.client == client_principal */
2613     if (increds.server)
2614         pkrb5_free_principal(ctx,increds.server);
2615     if (cc && (cc != alt_cc))
2616         pkrb5_cc_close(ctx, cc);
2617     if (ctx && (ctx != alt_ctx))
2618         pkrb5_free_context(ctx);
2619
2620    return(code);
2621 }
2622
2623 /**************************************/
2624 /* afs_realm_of_cell():               */
2625 /**************************************/
2626 static char *
2627 afs_realm_of_cell(afsconf_cell *cellconfig)
2628 {
2629     static char krbrlm[REALM_SZ+1]="";
2630     krb5_context  ctx = 0;
2631     char ** realmlist=NULL;
2632     krb5_error_code r;
2633
2634     if (!cellconfig)
2635         return 0;
2636
2637     r = pkrb5_init_context(&ctx); 
2638     if ( !r )
2639         r = pkrb5_get_host_realm(ctx, cellconfig->hostName[0], &realmlist);
2640     if ( !r && realmlist && realmlist[0] ) {
2641         strcpy(krbrlm, realmlist[0]);
2642         pkrb5_free_host_realm(ctx, realmlist);
2643     }
2644     if (ctx)
2645         pkrb5_free_context(ctx);
2646
2647     if ( !krbrlm[0] )
2648     {
2649         char *s = krbrlm;
2650         char *t = cellconfig->name;
2651         int c;
2652
2653         while (c = *t++)
2654         {
2655             if (islower(c)) c=toupper(c);
2656             *s++ = c;
2657         }
2658         *s++ = 0;
2659     }
2660     return(krbrlm);
2661 }
2662
2663 /**************************************/
2664 /* get_cellconfig():                  */
2665 /**************************************/
2666 static int 
2667 get_cellconfig(char *cell, afsconf_cell *cellconfig, char *local_cell)
2668 {
2669     int rc;
2670     char newcell[MAXCELLCHARS+1];
2671
2672     local_cell[0] = (char)0;
2673     memset(cellconfig, 0, sizeof(*cellconfig));
2674
2675     /* WIN32: cm_GetRootCellName(local_cell) - NOTE: no way to get max chars */
2676     if (rc = pcm_GetRootCellName(local_cell))
2677     {
2678         return(rc);
2679     }
2680
2681     if (strlen(cell) == 0)
2682         strcpy(cell, local_cell);
2683
2684     /* WIN32: cm_SearchCellFile(cell, pcallback, pdata) */
2685     strcpy(cellconfig->name, cell);
2686
2687     return pcm_SearchCellFile(cell, newcell, get_cellconfig_callback, (void*)cellconfig);
2688 }
2689
2690 /**************************************/
2691 /* get_cellconfig_callback():         */
2692 /**************************************/
2693 static long 
2694 get_cellconfig_callback(void *cellconfig, struct sockaddr_in *addrp, char *namep)
2695 {
2696     afsconf_cell *cc = (afsconf_cell *)cellconfig;
2697
2698     cc->hostAddr[cc->numServers] = *addrp;
2699     strcpy(cc->hostName[cc->numServers], namep);
2700     cc->numServers++;
2701     return(0);
2702 }
2703
2704
2705 /**************************************/
2706 /* KFW_AFS_error():                  */
2707 /**************************************/
2708 void
2709 KFW_AFS_error(LONG rc, LPCSTR FailedFunctionName)
2710 {
2711     char message[256];
2712     const char *errText; 
2713
2714     // Using AFS defines as error messages for now, until Transarc 
2715     // gets back to me with "string" translations of each of these 
2716     // const. defines. 
2717     if (rc == KTC_ERROR)
2718       errText = "KTC_ERROR";
2719     else if (rc == KTC_TOOBIG)
2720       errText = "KTC_TOOBIG";
2721     else if (rc == KTC_INVAL)
2722       errText = "KTC_INVAL";
2723     else if (rc == KTC_NOENT)
2724       errText = "KTC_NOENT";
2725     else if (rc == KTC_PIOCTLFAIL)
2726       errText = "KTC_PIOCTLFAIL";
2727     else if (rc == KTC_NOPIOCTL)
2728       errText = "KTC_NOPIOCTL";
2729     else if (rc == KTC_NOCELL)
2730       errText = "KTC_NOCELL";
2731     else if (rc == KTC_NOCM)
2732       errText = "KTC_NOCM: The service, Transarc AFS Daemon, most likely is not started!";
2733     else
2734       errText = "Unknown error!";
2735
2736     sprintf(message, "%s\n(%s failed)", errText, FailedFunctionName);
2737
2738     if ( IsDebuggerPresent() ) {
2739         OutputDebugString(message);
2740         OutputDebugString("\n");
2741     }
2742     MessageBox(NULL, message, "AFS", MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND);
2743     return;
2744 }
2745
2746 static DWORD 
2747 GetServiceStatus(
2748     LPSTR lpszMachineName, 
2749     LPSTR lpszServiceName,
2750     DWORD *lpdwCurrentState) 
2751
2752     DWORD           hr               = NOERROR; 
2753     SC_HANDLE       schSCManager     = NULL; 
2754     SC_HANDLE       schService       = NULL; 
2755     DWORD           fdwDesiredAccess = 0; 
2756     SERVICE_STATUS  ssServiceStatus  = {0}; 
2757     BOOL            fRet             = FALSE; 
2758
2759     *lpdwCurrentState = 0; 
2760  
2761     fdwDesiredAccess = GENERIC_READ; 
2762  
2763     schSCManager = OpenSCManager(lpszMachineName,  
2764                                  NULL,
2765                                  fdwDesiredAccess); 
2766  
2767     if(schSCManager == NULL) 
2768     { 
2769         hr = GetLastError();
2770         goto cleanup; 
2771     } 
2772  
2773     schService = OpenService(schSCManager,
2774                              lpszServiceName,
2775                              fdwDesiredAccess); 
2776  
2777     if(schService == NULL) 
2778     { 
2779         hr = GetLastError();
2780         goto cleanup; 
2781     } 
2782  
2783     fRet = QueryServiceStatus(schService,
2784                               &ssServiceStatus); 
2785  
2786     if(fRet == FALSE) 
2787     { 
2788         hr = GetLastError(); 
2789         goto cleanup; 
2790     } 
2791  
2792     *lpdwCurrentState = ssServiceStatus.dwCurrentState; 
2793  
2794 cleanup: 
2795  
2796     CloseServiceHandle(schService); 
2797     CloseServiceHandle(schSCManager); 
2798  
2799     return(hr); 
2800
2801
2802 void
2803 UnloadFuncs(
2804     FUNC_INFO fi[], 
2805     HINSTANCE h
2806     )
2807 {
2808     int n;
2809     if (fi)
2810         for (n = 0; fi[n].func_ptr_var; n++)
2811             *(fi[n].func_ptr_var) = 0;
2812     if (h) FreeLibrary(h);
2813 }
2814
2815 int
2816 LoadFuncs(
2817     const char* dll_name, 
2818     FUNC_INFO fi[], 
2819     HINSTANCE* ph,  // [out, optional] - DLL handle
2820     int* pindex,    // [out, optional] - index of last func loaded (-1 if none)
2821     int cleanup,    // cleanup function pointers and unload on error
2822     int go_on,      // continue loading even if some functions cannot be loaded
2823     int silent      // do not pop-up a system dialog if DLL cannot be loaded
2824     )
2825 {
2826     HINSTANCE h;
2827     int i, n, last_i;
2828     int error = 0;
2829     UINT em;
2830
2831     if (ph) *ph = 0;
2832     if (pindex) *pindex = -1;
2833
2834     for (n = 0; fi[n].func_ptr_var; n++)
2835         *(fi[n].func_ptr_var) = 0;
2836
2837     if (silent)
2838         em = SetErrorMode(SEM_FAILCRITICALERRORS);
2839     h = LoadLibrary(dll_name);
2840     if (silent)
2841         SetErrorMode(em);
2842
2843     if (!h)
2844         return 0;
2845
2846     last_i = -1;
2847     for (i = 0; (go_on || !error) && (i < n); i++)
2848     {
2849         void* p = (void*)GetProcAddress(h, fi[i].func_name);
2850         if (!p)
2851             error = 1;
2852         else
2853         {
2854             last_i = i;
2855             *(fi[i].func_ptr_var) = p;
2856         }
2857     }
2858     if (pindex) *pindex = last_i;
2859     if (error && cleanup && !go_on) {
2860         for (i = 0; i < n; i++) {
2861             *(fi[i].func_ptr_var) = 0;
2862         }
2863         FreeLibrary(h);
2864         return 0;
2865     }
2866     if (ph) *ph = h;
2867     if (error) return 0;
2868     return 1;
2869 }
2870
2871 #ifdef USE_FSPROBE
2872 // Cell Accessibility Functions
2873 // based on work originally submitted to the CMU Computer Club
2874 // by Jeffrey Hutzelman
2875 //
2876 // These would work great if the fsProbe interface had been 
2877 // ported to Windows
2878
2879 static 
2880 void probeComplete()
2881 {
2882     fsprobe_Cleanup(1);
2883     rx_Finalize();
2884 }
2885
2886 struct ping_params {
2887     unsigned short port;            // in
2888     int            retry_delay;     // in seconds
2889     int            verbose;         // in
2890     struct {
2891         int        wait;            // in seconds
2892         int        retry;           // in attempts
2893     }   host;
2894     int            max_hosts;       // in
2895     int            hosts_attempted; // out
2896 }
2897
2898 // the fsHandler is where we receive the answer to the probe
2899 static 
2900 int fsHandler(void)
2901 {
2902     ping_count = fsprobe_Results.probeNum;
2903     if (!*fsprobe_Results.probeOK)
2904     {
2905         ok_count++;
2906         if (waiting) complete();
2907     }
2908     if (ping_count == retry) 
2909         complete();
2910     return 0;
2911 }
2912
2913 // ping_fs is a callback routine meant to be called from within
2914 // cm_SearchCellFile() or cm_SearchCellDNS()
2915 static long 
2916 pingFS(void *ping_params, struct sockaddr_in *addrp, char *namep)
2917 {
2918     int rc;
2919     struct ping_params * pp = (struct ping_params *) ping_params;
2920
2921     if ( pp->max_hosts && pp->hosts_attempted >= pp->max_hosts )
2922         return 0;
2923
2924     pp->hosts_attempted++;
2925
2926     if (pp->port && addrp->sin_port != htons(pp->port))
2927         addrp->sin_port = htons(pp->port);
2928
2929     rc = fsprobe_Init(1, addrp, pp->retry_delay, fsHandler, pp->verbose);
2930     if (rc)
2931     {
2932         fprintf(stderr, "fsprobe_Init failed (%d)\n", rc);
2933         fsprobe_Cleanup(1);
2934         return 0;
2935     }
2936
2937     for (;;)
2938     {
2939         tv.tv_sec = pp->host.wait;
2940         tv.tv_usec = 0;
2941         if (IOMGR_Select(0, 0, 0, 0, &tv)) 
2942             break;
2943     }
2944     probeComplete();
2945     return(0);
2946 }
2947
2948
2949 static BOOL
2950 pingCell(char *cell)
2951 {
2952     int rc;
2953     char rootcell[MAXCELLCHARS+1];
2954     char newcell[MAXCELLCHARS+1];
2955     struct ping_params pp;
2956
2957     memset(&pp, 0, sizeof(struct ping_params));
2958
2959     if (!cell || strlen(cell) == 0) {
2960         /* WIN32 NOTE: no way to get max chars */
2961         if (rc = pcm_GetRootCellName(rootcell))
2962             return(FALSE);
2963         cell = rootcell;
2964     }
2965
2966     pp.port = 7000; // AFS FileServer
2967     pp.retry_delay = 10;
2968     pp.max_hosts = 3;
2969     pp.host.wait = 30;
2970     pp.host.retry = 0;
2971     pp.verbose = 1;
2972
2973     /* WIN32: cm_SearchCellFile(cell, pcallback, pdata) */
2974     rc = pcm_SearchCellFile(cell, newcell, pingFS, (void *)&pp);
2975 }
2976 #endif /* USE_FSPROBE */
2977  
2978 // These two items are imported from afscreds.h 
2979 // but it cannot be included without causing conflicts
2980 #define c100ns1SECOND        (LONGLONG)10000000
2981 static void 
2982 TimeToSystemTime (SYSTEMTIME *pst, time_t TimeT)
2983 {
2984     struct tm *pTime;
2985     memset (pst, 0x00, sizeof(SYSTEMTIME));
2986
2987     if ((pTime = localtime (&TimeT)) != NULL)
2988     {
2989         pst->wYear = pTime->tm_year + 1900;
2990         pst->wMonth = pTime->tm_mon + 1;
2991         pst->wDayOfWeek = pTime->tm_wday;
2992         pst->wDay = pTime->tm_mday;
2993         pst->wHour = pTime->tm_hour;
2994         pst->wMinute = pTime->tm_min;
2995         pst->wSecond = pTime->tm_sec;
2996         pst->wMilliseconds = 0;
2997     }
2998 }
2999
3000 void
3001 ObtainTokensFromUserIfNeeded(HWND hWnd)
3002 {
3003     char * rootcell = NULL;
3004     char   cell[MAXCELLCHARS+1] = "";
3005     char   password[PROBE_PASSWORD_LEN+1];
3006     krb5_data pwdata;
3007     afsconf_cell cellconfig;
3008     struct ktc_principal    aserver;
3009     struct ktc_principal    aclient;
3010     struct ktc_token    atoken;
3011     krb5_context ctx;
3012     krb5_timestamp now = 0;
3013     krb5_error_code code;
3014     int serverReachable = 0;
3015     int rc;
3016 #ifndef USE_FSPROBE
3017     krb5_ccache cc = 0;
3018     const char * realm = 0;
3019     krb5_principal principal = 0;
3020     char * pname = 0;
3021 #endif /* USE_FSPROBE */
3022     DWORD       CurrentState;
3023     char        HostName[64];
3024
3025     CurrentState = 0;
3026     memset(HostName, '\0', sizeof(HostName));
3027     gethostname(HostName, sizeof(HostName));
3028     if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
3029         return;
3030     if (CurrentState != SERVICE_RUNNING) {
3031         SendMessage(hWnd, WM_START_SERVICE, FALSE, 0L);
3032         return;
3033     }
3034
3035     if ( KFW_is_available() ) {
3036         code = pkrb5_init_context(&ctx);
3037         if ( code ) goto cleanup;
3038     }
3039
3040     rootcell = (char *)GlobalAlloc(GPTR,MAXCELLCHARS+1);
3041     if ( !rootcell ) goto cleanup;
3042
3043     code = get_cellconfig(cell, (void*)&cellconfig, rootcell);
3044     if ( code ) goto cleanup;
3045
3046     memset(&aserver, '\0', sizeof(aserver));
3047     strcpy(aserver.name, "afs");
3048     strcpy(aserver.cell, rootcell);
3049
3050     rc = pktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
3051
3052     if ( KFW_is_available() ) {
3053         code = pkrb5_timeofday(ctx, &now);
3054         if ( code ) 
3055             now = 0;
3056
3057         if (!rc && (now < atoken.endTime))
3058             goto cleanup;
3059     } else {
3060         SYSTEMTIME stNow;
3061         FILETIME ftNow;
3062         FILETIME ftExpires;
3063         LONGLONG llNow;
3064         LONGLONG llExpires;
3065         SYSTEMTIME stExpires;
3066
3067         TimeToSystemTime (&stExpires, atoken.endTime);
3068         GetLocalTime (&stNow);
3069         SystemTimeToFileTime (&stNow, &ftNow);
3070         SystemTimeToFileTime (&stExpires, &ftExpires);
3071
3072         llNow = (((LONGLONG)ftNow.dwHighDateTime) << 32) + (LONGLONG)(ftNow.dwLowDateTime);
3073         llExpires = (((LONGLONG)ftExpires.dwHighDateTime) << 32) + (LONGLONG)(ftExpires.dwLowDateTime);
3074
3075         llNow /= c100ns1SECOND;
3076         llExpires /= c100ns1SECOND;
3077
3078         if (!rc && (llNow < llExpires))
3079             goto cleanup;
3080     }
3081
3082
3083 #ifdef USE_FSPROBE
3084     serverReachable = cellPing(NULL);
3085 #else
3086     if ( KFW_is_available() ) {
3087         // If we can't use the FSProbe interface we can attempt to forge
3088         // a kinit and if we can back an invalid user error we know the
3089         // kdc is at least reachable
3090         realm = afs_realm_of_cell(&cellconfig);  // do not free
3091
3092         code = pkrb5_build_principal(ctx, &principal, strlen(realm),
3093                                      realm, PROBE_USERNAME, NULL, NULL);
3094         if ( code ) goto cleanup;
3095
3096         code = KFW_get_ccache(ctx, principal, &cc);
3097         if ( code ) goto cleanup;
3098
3099         code = pkrb5_unparse_name(ctx, principal, &pname);
3100         if ( code ) goto cleanup;
3101
3102         pwdata.data = password;
3103         pwdata.length = PROBE_PASSWORD_LEN;
3104         code = pkrb5_c_random_make_octets(ctx, &pwdata);
3105         if (code) {
3106             int i;
3107             for ( i=0 ; i<PROBE_PASSWORD_LEN ; i )
3108                 password[i] = 'x';
3109         }
3110         password[PROBE_PASSWORD_LEN] = '\0';
3111
3112         code = KFW_kinit(NULL, NULL, HWND_DESKTOP, 
3113                            pname, 
3114                            password,
3115                            5,
3116                            0,
3117                            0,
3118                            0,
3119                            1,
3120                            0);
3121         switch ( code ) {
3122         case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
3123         case KRB5KDC_ERR_CLIENT_REVOKED:
3124         case KRB5KDC_ERR_CLIENT_NOTYET:
3125         case KRB5KDC_ERR_PREAUTH_FAILED:
3126         case KRB5KDC_ERR_PREAUTH_REQUIRED:
3127         case KRB5KDC_ERR_PADATA_TYPE_NOSUPP:
3128             serverReachable = TRUE;
3129             break;
3130         default:
3131             serverReachable = FALSE;
3132         }
3133     } else {
3134         int i;
3135         for ( i=0 ; i<PROBE_PASSWORD_LEN ; i )
3136             password[i] = 'x';
3137
3138         code = ObtainNewCredentials(rootcell, PROBE_USERNAME, password);
3139         serverReachable = 1;
3140     }
3141 #endif
3142     if ( !serverReachable )
3143         goto cleanup;
3144
3145     if ( KFW_is_available() ) {
3146 #ifdef USE_MS2MIT
3147         KFW_import_windows_lsa();
3148 #endif /* USE_MS2MIT */
3149         KFW_AFS_renew_expiring_credentials();
3150         KFW_AFS_renew_token_for_cell(rootcell);
3151
3152         rc = pktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
3153         if (!rc && (now < atoken.endTime))
3154             goto cleanup;
3155     }
3156
3157     SendMessage(hWnd, WM_OBTAIN_TOKENS, FALSE, (long)rootcell);
3158     rootcell = NULL;    // rootcell freed by message receiver
3159
3160   cleanup:
3161     if (rootcell)
3162         GlobalFree(rootcell);
3163
3164 #ifndef USE_FSPROBE
3165     if ( pname )
3166         pkrb5_free_unparsed_name(ctx,pname);
3167     if ( principal )
3168         pkrb5_free_principal(ctx,principal);
3169     if (cc)
3170         pkrb5_cc_close(ctx,cc);
3171 #endif /* USE_FSPROBE */
3172     if (ctx)
3173         pkrb5_free_context(ctx);
3174     return;
3175 }
3176
3177 // IP Change Monitoring Functions
3178 #include <Iphlpapi.h>
3179
3180 DWORD
3181 GetNumOfIpAddrs(void)
3182 {
3183     PMIB_IPADDRTABLE pIpAddrTable = NULL;
3184     ULONG            dwSize;
3185     DWORD            code;
3186     DWORD            index;
3187     DWORD            validAddrs = 0;
3188
3189     dwSize = 0;
3190     code = GetIpAddrTable(NULL, &dwSize, 0);
3191     if (code == ERROR_INSUFFICIENT_BUFFER) {
3192         pIpAddrTable = malloc(dwSize);
3193         code = GetIpAddrTable(pIpAddrTable, &dwSize, 0);
3194         for ( index=0; index < pIpAddrTable->dwNumEntries; index++ ) {
3195             if (pIpAddrTable->table[index].dwAddr != 0)
3196                 validAddrs++;
3197         }
3198         free(pIpAddrTable);
3199     }
3200     return validAddrs;
3201 }
3202
3203 void
3204 IpAddrChangeMonitor(void * hWnd)
3205 {
3206 #ifdef USE_OVERLAPPED
3207     HANDLE Handle = INVALID_HANDLE_VALUE;
3208     OVERLAPPED Ovlap;
3209 #endif /* USE_OVERLAPPED */
3210     DWORD Result;
3211     DWORD prevNumOfAddrs = GetNumOfIpAddrs();
3212     DWORD NumOfAddrs;
3213
3214     if ( !hWnd )
3215         return;
3216
3217     while ( TRUE ) {
3218 #ifdef USE_OVERLAPPED
3219         ZeroMemory(&Ovlap, sizeof(OVERLAPPED));
3220
3221         Result = NotifyAddrChange(&Handle,&Ovlap);
3222         if (Result != ERROR_IO_PENDING)
3223         {        
3224             printf("NotifyAddrChange() failed with error %d \n", Result);
3225             break;
3226         }
3227
3228         if ((Result = WaitForSingleObject(Handle,INFINITE)) == WAIT_FAILED)
3229         {
3230             printf("WaitForSingleObject() failed with error %d\n",
3231                     GetLastError());
3232             continue;
3233         }
3234
3235         if (GetOverlappedResult(Handle, &Ovlap,
3236                                  &DataTransfered, TRUE) == 0)
3237         {
3238             printf("GetOverlapped result failed %d \n",
3239                     GetLastError());
3240             break;
3241         }
3242
3243 #else
3244         Result = NotifyAddrChange(NULL,NULL);
3245 #endif
3246         
3247         NumOfAddrs = GetNumOfIpAddrs();
3248         if ( NumOfAddrs != prevNumOfAddrs ) {
3249             // Give AFS Client Service a chance to notice and die
3250             // Or for network services to startup
3251             Sleep(2000);
3252             // this call should probably be mutex protected
3253             ObtainTokensFromUserIfNeeded(hWnd);
3254         }
3255         prevNumOfAddrs = NumOfAddrs;
3256     }
3257
3258 #ifdef USE_OVERLAPPED
3259     if (Handle != INVALID_HANDLE_VALUE)
3260         CloseHandle(Handle);
3261 #endif 
3262 }
3263
3264
3265 DWORD 
3266 IpAddrChangeMonitorInit(HWND hWnd)
3267 {
3268     DWORD status = ERROR_SUCCESS;
3269     HANDLE thread;
3270     ULONG  threadID = 0;
3271
3272     thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)IpAddrChangeMonitor,
3273                                     hWnd, 0, &threadID);
3274
3275     if (thread == NULL) {
3276         status = GetLastError();
3277     }
3278     CloseHandle(thread);
3279     return status;
3280 }
3281