kfw-thread-safety-20040315
[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
401     if ( !inited ) {
402         char mutexName[MAX_PATH];
403         HANDLE hMutex = NULL;
404
405         sprintf(mutexName, "AFS KFW Init pid=%d", getpid());
406         
407         hMutex = CreateMutex( NULL, TRUE, mutexName );
408         if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
409             if ( WaitForSingleObject( hMutex, INFINITE ) != WAIT_OBJECT_0 ) {
410                 return;
411             }
412         }
413         if ( !inited ) {
414             inited = 1;
415             LoadFuncs(KRB5_DLL, k5_fi, &hKrb5, 0, 1, 0, 0);
416             LoadFuncs(KRB4_DLL, k4_fi, &hKrb4, 0, 1, 0, 0);
417             LoadFuncs(COMERR_DLL, ce_fi, &hComErr, 0, 0, 1, 0);
418             LoadFuncs(SERVICE_DLL, service_fi, &hService, 0, 1, 0, 0);
419 #ifdef USE_MS2MIT
420             LoadFuncs(SECUR32_DLL, lsa_fi, &hSecur32, 0, 1, 1, 1);
421 #endif /* USE_MS2MIT */
422             LoadFuncs(KRB524_DLL, k524_fi, &hKrb524, 0, 1, 1, 1);
423             LoadFuncs(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0);
424             LoadFuncs(AFSTOKENS_DLL, afst_fi, &hAfsTokens, 0, 1, 0, 0);
425             LoadFuncs(AFSCONF_DLL, afsc_fi, &hAfsConf, 0, 1, 0, 0);
426             LoadFuncs(LEASH_DLL, leash_fi, &hLeash, 0, 1, 0, 0);
427             LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0);
428
429             if ( KFW_is_available() ) {
430                 char rootcell[MAXCELLCHARS+1];
431 #ifdef USE_MS2MIT
432                 KFW_import_windows_lsa();
433 #endif /* USE_MS2MIT */
434                 KFW_import_ccache_data();
435                 KFW_AFS_renew_expiring_tokens();
436
437                 /* WIN32 NOTE: no way to get max chars */
438                 if (!pcm_GetRootCellName(rootcell))
439                     KFW_AFS_renew_token_for_cell(rootcell);
440             }
441         }
442         ReleaseMutex(hMutex);
443         CloseHandle(hMutex);
444     }
445 }
446
447 void
448 KFW_cleanup(void)
449 {
450     if (hKrb5)
451         FreeLibrary(hKrb5);
452     if (hKrb4)
453         FreeLibrary(hKrb4);
454     if (hProfile)
455         FreeLibrary(hProfile);
456     if (hAfsTokens)
457         FreeLibrary(hAfsTokens);
458     if (hAfsConf)
459         FreeLibrary(hAfsConf);
460     if (hComErr)
461         FreeLibrary(hComErr);
462     if (hService)
463         FreeLibrary(hService);
464 #ifdef USE_MS2MIT
465     if (hSecur32)
466         FreeLibrary(hSecur32);
467 #endif /* USE_MS2MIT */
468     if (hKrb524)
469         FreeLibrary(hKrb524);
470     if (hLeash)
471         FreeLibrary(hLeash);
472     if (hCCAPI)
473         FreeLibrary(hCCAPI);
474 }
475
476 static char OpenAFSConfigKeyName[] = "SOFTWARE\\OpenAFS\\Client";
477
478 int 
479 KFW_is_available(void)
480 {
481     HKEY parmKey;
482         DWORD code, len;
483     DWORD enableKFW = 1;
484
485     code = RegOpenKeyEx(HKEY_CURRENT_USER, OpenAFSConfigKeyName,
486                          0, KEY_QUERY_VALUE, &parmKey);
487     if (code != ERROR_SUCCESS)
488         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, OpenAFSConfigKeyName,
489                              0, KEY_QUERY_VALUE, &parmKey);
490         if (code == ERROR_SUCCESS) {
491         len = sizeof(enableKFW);
492         code = RegQueryValueEx(parmKey, "EnableKFW", NULL, NULL,
493                                 (BYTE *) &enableKFW, &len);
494         if (code != ERROR_SUCCESS) {
495             enableKFW = 1;
496         }
497         RegCloseKey (parmKey);
498         }
499
500     if ( !enableKFW )
501         return FALSE;
502
503     KFW_initialize();
504     if ( hKrb5 && hComErr && hService && 
505 #ifdef USE_MS2MIT
506          hSecur32 && 
507 #endif /* USE_MS2MIT */
508          hKrb524 &&
509          hProfile && hAfsTokens && hAfsConf && hLeash && hCCAPI )
510         return TRUE;
511     return FALSE;
512 }
513
514 int 
515 KRB5_error(krb5_error_code rc, LPCSTR FailedFunctionName, 
516                  int FreeContextFlag, krb5_context * ctx, 
517                  krb5_ccache * cache)
518 {
519     char message[256];
520     const char *errText;
521     int krb5Error = ((int)(rc & 255));  
522     
523     /*
524     switch (krb5Error)
525     {
526         // Wrong password
527         case 31:
528         case 8:
529             return;
530     }
531     */
532         
533     errText = perror_message(rc);   
534     _snprintf(message, sizeof(message), 
535               "%s\n(Kerberos error %ld)\n\n%s failed", 
536               errText, 
537               krb5Error, 
538               FailedFunctionName);
539
540     if ( IsDebuggerPresent() )
541         OutputDebugString(message);
542
543     MessageBox(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR | 
544                MB_TASKMODAL | 
545                MB_SETFOREGROUND);
546     if (FreeContextFlag == 1)
547     {
548         if (ctx && *ctx != NULL)
549         {
550             if (cache && *cache != NULL) {
551                 pkrb5_cc_close(*ctx, *cache);
552                                 *cache = NULL;
553                         }
554         
555             pkrb5_free_context(*ctx);
556                         *ctx = NULL;
557         }
558     }
559
560     return rc;
561 }
562
563 void
564 KFW_AFS_update_princ_ccache_data(krb5_context ctx, krb5_ccache cc, int lsa)
565 {
566     struct principal_ccache_data * next = princ_cc_data;
567     krb5_principal principal = 0;
568     char * pname = NULL;
569     const char * ccname = NULL;
570     krb5_error_code code = 0;
571     krb5_error_code cc_code = 0;
572     krb5_cc_cursor cur;
573     krb5_creds creds;
574     krb5_flags flags=0;
575     krb5_timestamp now;
576
577     if (ctx == 0 || cc == 0)
578         return;
579
580     code = pkrb5_cc_get_principal(ctx, cc, &principal);
581     if ( code ) return;
582
583     code = pkrb5_unparse_name(ctx, principal, &pname);
584     if ( code ) goto cleanup;
585
586     ccname = pkrb5_cc_get_name(ctx, cc);
587     if (!ccname) goto cleanup;
588
589     // Search the existing list to see if we have a match 
590     if ( next ) {
591         for ( ; next ; next = next->next ) {
592             if ( !strcmp(next->principal,pname) && !strcmp(next->ccache_name, ccname) )
593                 break;
594         }
595     } 
596
597     // If not, match add a new node to the beginning of the list and assign init it
598     if ( !next ) {
599         next = (struct principal_ccache_data *) malloc(sizeof(struct principal_ccache_data));
600         next->next = princ_cc_data;
601         princ_cc_data = next;
602         next->principal = _strdup(pname);
603         next->ccache_name = _strdup(ccname);
604         next->from_lsa = lsa;
605         next->expired = 1;
606         next->expiration_time = 0;
607         next->renew = 0;
608     }
609
610     flags = 0;  // turn off OPENCLOSE mode
611     code = pkrb5_cc_set_flags(ctx, cc, flags);
612     if ( code ) goto cleanup;
613
614     code = pkrb5_timeofday(ctx, &now);
615
616     cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
617
618     while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {
619         if ( creds.ticket_flags & TKT_FLG_INITIAL ) {
620             int valid;
621             // we found the ticket we are looking for
622             // check validity of timestamp 
623             // We add a 5 minutes fudge factor to compensate for potential
624             // clock skew errors between the KDC and client OS
625
626             valid = ((creds.times.starttime > 0) &&
627                      now >= (creds.times.starttime - 300) &&
628                      now < (creds.times.endtime + 300) &&
629                      !(creds.ticket_flags & TKT_FLG_INVALID));
630
631             if ( next->from_lsa) {
632                 next->expired = 0;
633                 next->expiration_time = creds.times.endtime;
634                 next->renew = 1;
635             } else if ( valid ) {
636                 next->expired = 0;
637                 next->expiration_time = creds.times.endtime;
638                 next->renew = (creds.times.renew_till > creds.times.endtime) && 
639                                (creds.ticket_flags & TKT_FLG_RENEWABLE);
640             } else {
641                 next->expired = 1;
642                 next->expiration_time = 0;
643                 next->renew = 0;
644             }
645
646             pkrb5_free_cred_contents(ctx, &creds);
647             cc_code = KRB5_CC_END;
648             break;
649         }
650         pkrb5_free_cred_contents(ctx, &creds);
651     }
652
653     if (cc_code == KRB5_CC_END) {
654         code = pkrb5_cc_end_seq_get(ctx, cc, &cur);
655         if (code) goto cleanup;
656     }
657
658   cleanup:
659     flags = KRB5_TC_OPENCLOSE;  //turn on OPENCLOSE
660     code = pkrb5_cc_set_flags(ctx, cc, flags);
661
662     if ( pname )
663         pkrb5_free_unparsed_name(ctx,pname);
664     if ( principal )
665         pkrb5_free_principal(ctx,principal);
666 }   
667
668 int
669 KFW_AFS_find_ccache_for_principal(krb5_context ctx, char * principal, char **ccache, int valid_only)
670 {
671     struct principal_ccache_data * next = princ_cc_data;
672     char * response = NULL;
673
674     if ( !principal || !ccache )
675         return 0;
676
677     while ( next ) {
678         if ( (!valid_only || !next->expired) && !strcmp(next->principal,principal) ) {
679             if (response) {
680                 // we always want to prefer the MS Kerberos LSA cache or
681                 // the cache afscreds created specifically for the principal
682                 // if the current entry is either one, drop the previous find
683                 if ( next->from_lsa || !strcmp(next->ccache_name,principal) )
684                     free(response);
685             }
686             response = _strdup(next->ccache_name);
687             // MS Kerberos LSA is our best option so use it and quit
688             if ( next->from_lsa )   
689                 break;
690         }
691         next = next->next;
692     }
693
694     if ( response ) {
695         *ccache = response;
696         return 1;
697     }
698     return 0;
699 }
700
701 void 
702 KFW_AFS_delete_princ_ccache_data(krb5_context ctx, char * pname, char * ccname)
703 {
704     struct principal_ccache_data ** next = &princ_cc_data;
705
706     if ( !pname && !ccname )
707         return;
708
709     while ( (*next) ) {
710         if ( !strcmp((*next)->principal,pname) || 
711              !strcmp((*next)->ccache_name,ccname) ) {
712             void * temp;
713             free((*next)->principal);
714             free((*next)->ccache_name);
715             temp = (*next);
716             (*next) = (*next)->next;
717             free(temp);
718         }
719     }
720 }
721
722 void 
723 KFW_AFS_update_cell_princ_map(krb5_context ctx, char * cell, char *pname, int active)
724 {
725     struct cell_principal_map * next = cell_princ_map;
726
727     // Search the existing list to see if we have a match 
728     if ( next ) {
729         for ( ; next ; next = next->next ) {
730             if ( !strcmp(next->cell, cell) ) {
731                 if ( !strcmp(next->principal,pname) ) {
732                     next->active = active;
733                                         break;
734                 } else {
735                     // OpenAFS currently has a restriction of one active token per cell
736                     // Therefore, whenever we update the table with a new active cell we
737                     // must mark all of the other principal to cell entries as inactive.
738                     if (active)
739                         next->active = 0;
740                 }
741             }
742         }
743     } 
744
745     // If not, match add a new node to the beginning of the list and assign init it
746     if ( !next ) {
747         next = (struct cell_principal_map *) malloc(sizeof(struct cell_principal_map));
748         next->next = cell_princ_map;
749         cell_princ_map = next;
750         next->principal = _strdup(pname);
751         next->cell = _strdup(cell);
752         next->active = active;
753     }
754 }
755
756 void 
757 KFW_AFS_delete_cell_princ_maps(krb5_context ctx, char * pname, char * cell)
758 {
759     struct cell_principal_map ** next = &cell_princ_map;
760
761     if ( !pname && !cell )
762         return;
763
764     while ( (*next) ) {
765         if ( !strcmp((*next)->principal,pname) || 
766              !strcmp((*next)->cell,cell) ) {
767             void * temp;
768             free((*next)->principal);
769             free((*next)->cell);
770             temp = (*next);
771             (*next) = (*next)->next;
772             free(temp);
773         }
774     }
775 }
776
777 // Returns (if possible) a principal which has been known in 
778 // the past to have been used to obtain tokens for the specified
779 // cell.  
780 // TODO: Attempt to return one which has not yet expired by checking
781 // the principal/ccache data
782 int
783 KFW_AFS_find_principals_for_cell(krb5_context ctx, char * cell, char **principals[], int active_only)
784 {
785     struct cell_principal_map * next_map = cell_princ_map;
786     const char * princ = NULL;
787     int count = 0, i;
788
789     if ( !cell )
790         return 0;
791
792     while ( next_map ) {
793         if ( (!active_only || next_map->active) && !strcmp(next_map->cell,cell) ) {
794             count++;
795         }
796         next_map = next_map->next;
797     }
798
799     if ( !principals )
800         return count;
801
802     *principals = (char **) malloc(sizeof(char *) * count);
803     for ( next_map = cell_princ_map, i=0 ; next_map && i<count; next_map = next_map->next )
804     {
805         if ( (!active_only || next_map->active) && !strcmp(next_map->cell,cell) ) {
806             (*principals)[i++] = _strdup(next_map->principal);
807         }
808     }
809     return count;
810 }
811
812 int
813 KFW_AFS_find_cells_for_princ(krb5_context ctx, char * pname, char **cells[], int active_only)
814 {
815     int     count = 0, i;
816     struct cell_principal_map * next_map = cell_princ_map;
817     const char * princ = NULL;
818     
819     if ( !pname )
820         return 0;
821
822     while ( next_map ) {
823         if ( (!active_only || next_map->active) && !strcmp(next_map->principal,pname) ) {
824             count++;
825         }
826         next_map = next_map->next;
827     }
828
829     if ( !cells )
830         return count;
831
832     *cells = (char **) malloc(sizeof(char *) * count);
833     for ( next_map = cell_princ_map, i=0 ; next_map && i<count; next_map = next_map->next )
834     {
835         if ( (!active_only || next_map->active) && !strcmp(next_map->principal,pname) ) {
836             (*cells)[i++] = _strdup(next_map->cell);
837         }
838     }
839     return count;
840 }
841
842 /* Given a principal return an existing ccache or create one and return */
843 int
844 KFW_get_ccache(krb5_context alt_ctx, krb5_principal principal, krb5_ccache * cc)
845 {
846     krb5_context ctx;
847     char * pname = 0;
848     char * ccname = 0;
849     krb5_error_code code;
850
851     if (!pkrb5_init_context)
852         return 0;
853
854     if ( alt_ctx ) {
855         ctx = alt_ctx;
856     } else {
857         code = pkrb5_init_context(&ctx);
858         if (code) goto cleanup;
859     }
860
861     if ( principal ) {
862         code = pkrb5_unparse_name(ctx, principal, &pname);
863         if (code) goto cleanup;
864
865         if ( !KFW_AFS_find_ccache_for_principal(ctx,pname,&ccname,TRUE) &&
866              !KFW_AFS_find_ccache_for_principal(ctx,pname,&ccname,FALSE)) {
867             ccname = (char *)malloc(strlen(pname) + 5);
868             sprintf(ccname,"API:%s",pname);
869         }
870         code = pkrb5_cc_resolve(ctx, ccname, cc);
871     } else {
872         code = pkrb5_cc_default(ctx, cc);
873         if (code) goto cleanup;
874     }
875
876   cleanup:
877     if (ccname)
878         free(ccname);
879     if (pname)
880         pkrb5_free_unparsed_name(ctx,pname);
881     if (ctx && (ctx != alt_ctx))
882         pkrb5_free_context(ctx);
883     return(code);
884 }
885
886 #ifdef USE_MS2MIT
887 // Import Microsoft Credentials into a new MIT ccache
888 void
889 KFW_import_windows_lsa(void)
890 {
891     krb5_context ctx = 0;
892     krb5_ccache  cc = 0;
893     krb5_principal princ = 0;
894     char * pname = NULL;
895     krb5_data *  realm;
896     krb5_error_code code;
897     char cell[128]="";
898     int i;
899          
900     if (!pkrb5_init_context)
901         return;
902
903     if ( !MSLSA_IsKerberosLogon() )
904         return;
905
906     code = pkrb5_init_context(&ctx);
907     if (code) goto cleanup;
908
909     code = pkrb5_cc_resolve(ctx, LSA_CCNAME, &cc);
910     if (code) goto cleanup;
911
912     KFW_AFS_update_princ_ccache_data(ctx, cc, TRUE);
913
914     code = pkrb5_cc_get_principal(ctx, cc, &princ);
915     if ( code ) goto cleanup;
916
917     code = pkrb5_unparse_name(ctx,princ,&pname);
918     if ( code ) goto cleanup;
919
920     realm = krb5_princ_realm(ctx, princ);
921     for ( i=0; i<realm->length; i++ ) {
922         cell[i] = tolower(realm->data[i]);
923     }
924         cell[i] = '\0';
925
926     code = KFW_AFS_klog(ctx, cc, "afs", cell, realm->data, pLeash_get_default_lifetime());
927     if ( IsDebuggerPresent() ) {
928         char message[256];
929         sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
930         OutputDebugString(message);
931     }
932     if ( code ) goto cleanup;
933
934     KFW_AFS_update_cell_princ_map(ctx, cell, pname, TRUE);
935
936   cleanup:
937     if (pname)
938         pkrb5_free_unparsed_name(ctx,pname);
939     if (princ)
940         pkrb5_free_principal(ctx,princ);
941     if (cc)
942         pkrb5_cc_close(ctx,cc);
943     if (ctx)
944         pkrb5_free_context(ctx);
945 }
946 #endif /* USE_MS2MIT */
947
948 // If there are existing MIT credentials, copy them to a new
949 // ccache named after the principal
950
951 // Enumerate all existing MIT ccaches and construct entries
952 // in the principal_ccache table
953
954 // Enumerate all existing AFS Tokens and construct entries
955 // in the cell_principal table
956 void
957 KFW_import_ccache_data(void)
958 {
959     krb5_context ctx = 0;
960     krb5_ccache  cc = 0;
961     krb5_principal principal = 0;
962     krb5_creds creds;
963     krb5_error_code code;
964     krb5_error_code cc_code;
965     krb5_cc_cursor cur;
966     apiCB * cc_ctx = 0;
967     struct _infoNC ** pNCi = NULL;
968     int i, j, flags;
969
970     if ( !pcc_initialize )
971         return;
972
973     if ( IsDebuggerPresent() )
974         OutputDebugString("KFW_import_ccache_data()\n");
975
976     code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL);
977     if (code) goto cleanup;
978
979     code = pcc_get_NC_info(cc_ctx, &pNCi);
980     if (code) goto cleanup;
981
982     code = pkrb5_init_context(&ctx);
983     if (code) goto cleanup;
984
985     for ( i=0; pNCi[i]; i++ ) {
986         if ( pNCi[i]->vers != CC_CRED_V5 )
987             continue;
988         if ( IsDebuggerPresent() ) {
989             OutputDebugString("Principal: ");
990             OutputDebugString(pNCi[i]->principal);
991             OutputDebugString(" in ccache ");
992             OutputDebugString(pNCi[i]->name);
993             OutputDebugString("\n");
994         }
995         if ( strcmp(pNCi[i]->name,pNCi[i]->principal)
996              && strcmp(pNCi[i]->name,LSA_CCNAME) 
997              ) {
998             int found = 0;
999             krb5_ccache oldcc = 0;
1000             for ( j=0; pNCi[j]; j++ ) {
1001                 if (!strcmp(pNCi[j]->name,pNCi[i]->principal)) {
1002                     found = 1;
1003                     break;
1004                 }
1005             }
1006             if (found)
1007                 continue;
1008
1009             if ( IsDebuggerPresent() )
1010                 OutputDebugString("copying ccache data to new ccache\n");
1011
1012             code = pkrb5_cc_resolve(ctx, pNCi[i]->principal, &cc);
1013             if (code) goto loop_cleanup;
1014             code = pkrb5_parse_name(ctx, pNCi[i]->principal, &principal);
1015             if (code) goto loop_cleanup;
1016             code = pkrb5_cc_initialize(ctx, cc, principal);
1017             if (code) goto loop_cleanup;
1018             code = pkrb5_cc_resolve(ctx, pNCi[i]->name, &oldcc);
1019             if (code) goto loop_cleanup;
1020             code = pkrb5_cc_copy_creds(ctx,oldcc,cc);
1021                         if (code) {
1022                                 code = pkrb5_cc_close(ctx,cc);
1023                 cc = 0;
1024                 code = pkrb5_cc_close(ctx,oldcc);
1025                 cc = 0;
1026                                 KRB5_error(code, "krb5_cc_copy_creds", 0, NULL, NULL);
1027                                 continue;
1028                         }
1029             code = pkrb5_cc_close(ctx,oldcc);
1030         } else {
1031             code = pkrb5_cc_resolve(ctx, pNCi[i]->name, &cc);
1032             if (code) goto loop_cleanup;
1033         }
1034
1035         flags = 0;  // turn off OPENCLOSE mode
1036         code = pkrb5_cc_set_flags(ctx, cc, flags);
1037         if ( code ) goto cleanup;
1038
1039         KFW_AFS_update_princ_ccache_data(ctx, cc, !strcmp(pNCi[i]->name,LSA_CCNAME));
1040
1041         cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
1042
1043         while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {
1044             krb5_data * sname = krb5_princ_name(ctx, creds.server);
1045             krb5_data * cell  = krb5_princ_component(ctx, creds.server, 1);
1046             krb5_data * realm = krb5_princ_realm(ctx, creds.server);
1047             if ( sname && cell && !strcmp("afs",sname->data) ) {
1048                 struct ktc_principal    aserver;
1049                 struct ktc_principal    aclient;
1050                 struct ktc_token        atoken;
1051                 int active = TRUE;
1052
1053                 if ( IsDebuggerPresent() )  {
1054                     OutputDebugString("Found AFS ticket: ");
1055                     OutputDebugString(sname->data);
1056                     if ( cell->data ) {
1057                         OutputDebugString("/");
1058                         OutputDebugString(cell->data);
1059                     }
1060                     OutputDebugString("@");
1061                     OutputDebugString(realm->data);
1062                     OutputDebugString("\n");
1063                 }
1064
1065                 memset(&aserver, '\0', sizeof(aserver));
1066                 strcpy(aserver.name, sname->data);
1067                 strcpy(aserver.cell, cell->data);
1068
1069                 code = pktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
1070                 if (!code) {
1071                     // Found a token in AFS Client Server which matches
1072                     char pname[128], *p, *q;
1073                     for ( p=pname, q=aclient.name; *q; p++, q++)
1074                         *p = *q;
1075                     for ( *p++ = '@', q=aclient.cell; *q; p++, q++)
1076                         *p = toupper(*q);
1077                     *p = '\0';
1078
1079                     if ( IsDebuggerPresent() )  {
1080                         OutputDebugString("Found AFS token: ");
1081                         OutputDebugString(pname);
1082                         OutputDebugString("\n");
1083                     }
1084
1085                     if ( strcmp(pname,pNCi[i]->principal)  )
1086                         active = FALSE;
1087                     KFW_AFS_update_cell_princ_map(ctx, cell->data, pNCi[i]->principal, active);
1088                 } else {
1089                     // Attempt to import it
1090                     KFW_AFS_update_cell_princ_map(ctx, cell->data, pNCi[i]->principal, active);
1091
1092                     if ( IsDebuggerPresent() )  {
1093                         OutputDebugString("Calling KFW_AFS_klog() to obtain token\n");
1094                     }
1095
1096                     code = KFW_AFS_klog(ctx, cc, "afs", cell->data, realm->data, pLeash_get_default_lifetime());
1097                     if ( IsDebuggerPresent() ) {
1098                         char message[256];
1099                         sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1100                         OutputDebugString(message);
1101                     }
1102                 }
1103             } else if ( IsDebuggerPresent() ) {
1104                 OutputDebugString("Found ticket: ");
1105                 OutputDebugString(sname->data);
1106                 if ( cell && cell->data ) {
1107                     OutputDebugString("/");
1108                     OutputDebugString(cell->data);
1109                 }
1110                 OutputDebugString("@");
1111                 OutputDebugString(realm->data);
1112                 OutputDebugString("\n");
1113             }
1114             pkrb5_free_cred_contents(ctx, &creds);
1115         }
1116
1117         if (cc_code == KRB5_CC_END) {
1118             cc_code = pkrb5_cc_end_seq_get(ctx, cc, &cur);
1119             if (cc_code) goto loop_cleanup;
1120         }
1121
1122       loop_cleanup:
1123         flags = KRB5_TC_OPENCLOSE;  //turn on OPENCLOSE
1124         code = pkrb5_cc_set_flags(ctx, cc, flags);
1125         if (cc) {
1126             pkrb5_cc_close(ctx,cc);
1127             cc = 0;
1128         }
1129     }
1130
1131   cleanup:
1132     if (principal)
1133         pkrb5_free_principal(ctx,principal);
1134     if (ctx)
1135         pkrb5_free_context(ctx);
1136     if (pNCi)
1137         pcc_free_NC_info(cc_ctx, &pNCi);
1138     if (cc_ctx)
1139         pcc_shutdown(&cc_ctx);
1140 }
1141
1142
1143 int
1144 KFW_AFS_get_cred(char * username, 
1145                   char * instance, 
1146                   char * cell,
1147                   char * password,
1148                   int lifetime,
1149                   char ** reasonP )
1150 {
1151     krb5_context ctx = 0;
1152     krb5_ccache cc = 0;
1153     char * realm = 0;
1154     char ** realmlist = 0;
1155     krb5_principal principal = 0;
1156     char * pname = 0;
1157     krb5_error_code code;
1158         char local_cell[MAXCELLCHARS+1];
1159     char **cells = NULL;
1160     int  cell_count=0;
1161     afsconf_cell cellconfig;
1162
1163     if (!pkrb5_init_context)
1164         return 0;
1165
1166     if ( IsDebuggerPresent() ) {
1167         OutputDebugString("KFW_AFS_get_cred for token ");
1168         OutputDebugString(username);
1169         if ( instance ) {
1170             OutputDebugString("/");
1171             OutputDebugString(instance);
1172         }
1173         OutputDebugString("@");
1174         OutputDebugString(cell);
1175         OutputDebugString("\n");
1176     }
1177
1178     code = pkrb5_init_context(&ctx);
1179     if ( code ) goto cleanup;
1180
1181     code = get_cellconfig( cell, (void*)&cellconfig, local_cell);
1182     if ( code ) goto cleanup;
1183
1184     realm = afs_realm_of_cell(&cellconfig);  // do not free
1185
1186     if ( IsDebuggerPresent() ) {
1187         OutputDebugString("Realm: ");
1188         OutputDebugString(realm);
1189         OutputDebugString("\n");
1190     }
1191
1192     code = pkrb5_build_principal(ctx, &principal, strlen(realm),
1193                                  realm, username, 
1194                                  (instance && instance[0]) ? instance : NULL, 
1195                                  NULL);
1196
1197     code = KFW_get_ccache(ctx, principal, &cc);
1198     if ( code ) goto cleanup;
1199
1200     code = pkrb5_unparse_name(ctx, principal, &pname);
1201     if ( code ) goto cleanup;
1202
1203     if ( lifetime == 0 )
1204         lifetime = pLeash_get_default_lifetime();
1205
1206     code = KFW_kinit(ctx, cc, HWND_DESKTOP, 
1207                       pname, 
1208                       password,
1209                       lifetime,
1210                       pLeash_get_default_forwardable(),
1211                       pLeash_get_default_proxiable(),
1212                       pLeash_get_default_renewable() ? pLeash_get_default_renew_till() : 0,
1213                       pLeash_get_default_noaddresses(),
1214                       pLeash_get_default_publicip());
1215     if ( IsDebuggerPresent() ) {
1216         char message[256];
1217         sprintf(message,"KFW_kinit() returns: %d\n",code);
1218         OutputDebugString(message);
1219     }
1220     if ( code ) goto cleanup;
1221                    
1222     KFW_AFS_update_princ_ccache_data(ctx, cc, FALSE);
1223
1224     code = KFW_AFS_klog(ctx, cc, "afs", cell, realm, lifetime);
1225     if ( IsDebuggerPresent() ) {
1226         char message[256];
1227         sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1228         OutputDebugString(message);
1229     }
1230     if ( code ) goto cleanup;
1231
1232     KFW_AFS_update_cell_princ_map(ctx, cell, pname, TRUE);
1233
1234     // Attempt to obtain new tokens for other cells supported by the same 
1235     // principal
1236     cell_count = KFW_AFS_find_cells_for_princ(ctx, pname, &cells, TRUE);
1237     if ( cell_count > 1 ) {
1238         while ( cell_count-- ) {
1239             if ( strcmp(cells[cell_count],cell) ) {
1240                 if ( IsDebuggerPresent() ) {
1241                     char message[256];
1242                     sprintf(message,"found another cell for the same principal: %s\n",cell);
1243                     OutputDebugString(message);
1244                 }
1245                 code = get_cellconfig( cells[cell_count], (void*)&cellconfig, local_cell);
1246                 if ( code ) continue;
1247     
1248                 realm = afs_realm_of_cell(&cellconfig);  // do not free
1249                 if ( IsDebuggerPresent() ) {
1250                     OutputDebugString("Realm: ");
1251                     OutputDebugString(realm);
1252                     OutputDebugString("\n");
1253                 }
1254                 
1255                 code = KFW_AFS_klog(ctx, cc, "afs", cells[cell_count], realm, lifetime);
1256                 if ( IsDebuggerPresent() ) {
1257                     char message[256];
1258                     sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1259                     OutputDebugString(message);
1260                 }
1261             }
1262             free(cells[cell_count]);
1263         }
1264         free(cells);
1265     } else if ( cell_count == 1 ) {
1266         free(cells[0]);
1267         free(cells);
1268     }
1269
1270   cleanup:
1271     if ( pname )
1272         pkrb5_free_unparsed_name(ctx,pname);
1273     if ( cc )
1274         pkrb5_cc_close(ctx, cc);
1275
1276     if ( code && reasonP ) {
1277         *reasonP = (char *)perror_message(code);
1278     }
1279     return(code);
1280 }
1281
1282 int 
1283 KFW_AFS_destroy_tickets_for_cell(char * cell)
1284 {
1285     krb5_context                ctx = 0;
1286     krb5_error_code             code;
1287     int count;
1288     char ** principals = NULL;
1289
1290     if (!pkrb5_init_context)
1291         return 0;
1292
1293     if ( IsDebuggerPresent() ) {
1294         OutputDebugString("KFW_AFS_destroy_ticets_for_cell: ");
1295         OutputDebugString(cell);
1296         OutputDebugString("\n");
1297     }
1298
1299     code = pkrb5_init_context(&ctx);
1300     if (code) ctx = 0;
1301
1302     count = KFW_AFS_find_principals_for_cell(ctx, cell, &principals, FALSE);
1303     if ( count > 0 ) {
1304         krb5_principal      princ = 0;
1305         krb5_ccache                     cc  = 0;
1306
1307         while ( count-- ) {
1308             int cell_count = KFW_AFS_find_cells_for_princ(ctx, principals[count], NULL, TRUE);
1309             if ( cell_count > 1 ) {
1310                 // TODO - What we really should do here is verify whether or not any of the
1311                 // other cells which use this principal to obtain its credentials actually
1312                 // have valid tokens or not.  If they are currently using these credentials
1313                 // we will skip them.  For the time being we assume that if there is an active
1314                 // map in the table that they are actively being used.
1315                 goto loop_cleanup;
1316             }
1317
1318             code = pkrb5_parse_name(ctx, principals[count], &princ);
1319             if (code) goto loop_cleanup;
1320
1321             code = KFW_get_ccache(ctx, princ, &cc);
1322             if (code) goto loop_cleanup;
1323
1324             code = pkrb5_cc_destroy(ctx, cc);
1325             if (!code) cc = 0;
1326
1327           loop_cleanup:
1328             if ( cc ) {
1329                 pkrb5_cc_close(ctx, cc);
1330                 cc = 0;
1331             }
1332             if ( princ ) {
1333                 pkrb5_free_principal(ctx, princ);
1334                 princ = 0;
1335             }
1336
1337             KFW_AFS_update_cell_princ_map(ctx, cell, principals[count], FALSE);
1338             free(principals[count]);
1339         }
1340         free(principals);
1341     }
1342     pkrb5_free_context(ctx);
1343     return 0;
1344 }
1345
1346 int
1347 KFW_AFS_renew_expiring_tokens(void)
1348 {
1349     krb5_error_code                     code = 0;
1350     krb5_context                        ctx = 0;
1351     krb5_ccache                         cc = 0;
1352     krb5_timestamp now;
1353     struct principal_ccache_data * pcc_next = princ_cc_data;
1354     int cell_count;
1355     char ** cells=NULL;
1356     const char * realm = NULL;
1357     char local_cell[MAXCELLCHARS+1]="";
1358     afsconf_cell cellconfig;
1359
1360     if (!pkrb5_init_context)
1361         return 0;
1362
1363     if ( pcc_next == NULL ) // nothing to do
1364         return 0;
1365
1366     if ( IsDebuggerPresent() ) {
1367         OutputDebugString("KFW_AFS_renew_expiring_tokens\n");
1368     }
1369
1370     code = pkrb5_init_context(&ctx);
1371     if (code) goto cleanup;
1372
1373     code = pkrb5_timeofday(ctx, &now);
1374     if (code) goto cleanup; 
1375
1376     for ( ; pcc_next ; pcc_next = pcc_next->next ) {
1377         if ( pcc_next->expired ) 
1378             continue;
1379
1380         if ( now >= (pcc_next->expiration_time) ) {
1381             if ( !pcc_next->from_lsa ) {
1382                 pcc_next->expired = 1;
1383                 continue;
1384             }
1385         }
1386
1387         if ( pcc_next->renew && now >= (pcc_next->expiration_time - cminRENEW * csec1MINUTE) ) {
1388             code = pkrb5_cc_resolve(ctx, pcc_next->ccache_name, &cc);
1389             if ( code ) 
1390                                 goto loop_cleanup;
1391             code = KFW_renew(ctx,cc);
1392 #ifdef USE_MS2MIT
1393             if ( code && pcc_next->from_lsa)
1394                 goto loop_cleanup;
1395 #endif /* USE_MS2MIT */
1396
1397
1398             KFW_AFS_update_princ_ccache_data(ctx, cc, pcc_next->from_lsa);
1399             if (code) goto loop_cleanup;
1400
1401             // Attempt to obtain new tokens for other cells supported by the same 
1402             // principal
1403             cell_count = KFW_AFS_find_cells_for_princ(ctx, pcc_next->principal, &cells, TRUE);
1404             if ( cell_count > 0 ) {
1405                 while ( cell_count-- ) {
1406                     if ( IsDebuggerPresent() ) {
1407                         OutputDebugString("Cell: ");
1408                         OutputDebugString(cells[cell_count]);
1409                         OutputDebugString("\n");
1410                     }
1411                     code = get_cellconfig( cells[cell_count], (void*)&cellconfig, local_cell);
1412                     if ( code ) continue;
1413                     realm = afs_realm_of_cell(&cellconfig);  // do not free
1414                     if ( IsDebuggerPresent() ) {
1415                         OutputDebugString("Realm: ");
1416                         OutputDebugString(realm);
1417                         OutputDebugString("\n");
1418                     }
1419                     code = KFW_AFS_klog(ctx, cc, "afs", cells[cell_count], (char *)realm, pLeash_get_default_lifetime());
1420                     if ( IsDebuggerPresent() ) {
1421                         char message[256];
1422                         sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1423                         OutputDebugString(message);
1424                     }
1425                     free(cells[cell_count]);
1426                 }
1427                 free(cells);
1428             }
1429         }
1430
1431       loop_cleanup:
1432         if ( cc ) {
1433             pkrb5_cc_close(ctx,cc);
1434             cc = 0;
1435         }
1436     }
1437
1438   cleanup:
1439     if ( cc )
1440         pkrb5_cc_close(ctx,cc);
1441     if ( ctx )
1442         pkrb5_free_context(ctx);
1443
1444     return 0;
1445 }
1446
1447
1448 BOOL
1449 KFW_AFS_renew_token_for_cell(char * cell)
1450 {
1451     krb5_error_code                     code = 0;
1452     krb5_context                        ctx = 0;
1453     int count;
1454     char ** principals = NULL;
1455
1456     if (!pkrb5_init_context)
1457         return 0;
1458
1459     if ( IsDebuggerPresent() ) {
1460         OutputDebugString("KFW_AFS_renew_token_for_cell:");
1461         OutputDebugString(cell);
1462         OutputDebugString("\n");
1463     }
1464
1465     code = pkrb5_init_context(&ctx);
1466     if (code) goto cleanup;
1467
1468     count = KFW_AFS_find_principals_for_cell(ctx, cell, &principals, TRUE);
1469     if ( count > 0 ) {
1470         krb5_principal      princ = 0;
1471         krb5_principal      service = 0;
1472 #ifdef COMMENT
1473         krb5_creds          mcreds, creds;
1474 #endif /* COMMENT */
1475         krb5_ccache                     cc  = 0;
1476         const char * realm = NULL;
1477         afsconf_cell cellconfig;
1478         char local_cell[MAXCELLCHARS+1];
1479
1480         while ( count-- ) {
1481             code = pkrb5_parse_name(ctx, principals[count], &princ);
1482             if (code) goto loop_cleanup;
1483
1484             code = KFW_get_ccache(ctx, princ, &cc);
1485             if (code) goto loop_cleanup;
1486
1487             code = get_cellconfig( cell, (void*)&cellconfig, local_cell);
1488             if ( code ) goto loop_cleanup;
1489
1490             realm = afs_realm_of_cell(&cellconfig);  // do not free
1491             if ( IsDebuggerPresent() ) {
1492                 OutputDebugString("Realm: ");
1493                 OutputDebugString(realm);
1494                 OutputDebugString("\n");
1495             }
1496
1497 #ifdef COMMENT
1498             /* krb5_cc_remove_cred() is not implemented 
1499              * for a single cred 
1500              */
1501             code = pkrb5_build_principal(ctx, &service, strlen(realm),
1502                                           realm, "afs", cell, NULL);
1503             if (!code) {
1504                 memset(&mcreds, 0, sizeof(krb5_creds));
1505                 mcreds.client = princ;
1506                 mcreds.server = service;
1507
1508                 code = pkrb5_cc_retrieve_cred(ctx, cc, 0, &mcreds, &creds);
1509                 if (!code) {
1510                     if ( IsDebuggerPresent() ) {
1511                         char * cname, *sname;
1512                         pkrb5_unparse_name(ctx, creds.client, &cname);
1513                         pkrb5_unparse_name(ctx, creds.server, &sname);
1514                         OutputDebugString("Removing credential for client \"");
1515                         OutputDebugString(cname);
1516                         OutputDebugString("\" and service \"");
1517                         OutputDebugString(sname);
1518                         OutputDebugString("\"\n");
1519                         pkrb5_free_unparsed_name(ctx,cname);
1520                         pkrb5_free_unparsed_name(ctx,sname);
1521                     }
1522
1523                     code = pkrb5_cc_remove_cred(ctx, cc, 0, &creds);
1524                     pkrb5_free_principal(ctx, creds.client);
1525                     pkrb5_free_principal(ctx, creds.server);
1526                 }
1527             }
1528 #endif /* COMMENT */
1529
1530             code = KFW_AFS_klog(ctx, cc, "afs", cell, (char *)realm, pLeash_get_default_lifetime());
1531             if ( IsDebuggerPresent() ) {
1532                 char message[256];
1533                 sprintf(message,"KFW_AFS_klog() returns: %d\n",code);
1534                 OutputDebugString(message);
1535             }
1536
1537           loop_cleanup:
1538             if (cc) {
1539                 pkrb5_cc_close(ctx, cc);
1540                 cc = 0;
1541             }
1542             if (princ) {
1543                 pkrb5_free_principal(ctx, princ);
1544                 princ = 0;
1545             }
1546             if (service) {
1547                 pkrb5_free_principal(ctx, service);
1548                 princ = 0;
1549             }
1550
1551             KFW_AFS_update_cell_princ_map(ctx, cell, principals[count], code ? FALSE : TRUE);
1552             free(principals[count]);
1553         }
1554         free(principals);
1555     } else
1556         code = -1;      // we did not renew the tokens 
1557
1558   cleanup:
1559     pkrb5_free_context(ctx);
1560     return (code ? FALSE : TRUE);
1561
1562 }
1563
1564 int
1565 KFW_AFS_renew_tokens_for_all_cells(void)
1566 {
1567     struct cell_principal_map * next = cell_princ_map;
1568
1569     if ( IsDebuggerPresent() )
1570         OutputDebugString("KFW_AFS_renew_tokens_for_all()\n");
1571
1572     if ( !next )
1573         return 0;
1574
1575     for ( ; next ; next = next->next ) {
1576         if ( next->active )
1577             KFW_AFS_renew_token_for_cell(next->cell);
1578     }
1579     return 0;
1580 }
1581
1582 int
1583 KFW_renew(krb5_context alt_ctx, krb5_ccache alt_cc)
1584 {
1585     krb5_error_code                     code = 0;
1586     krb5_context                        ctx = 0;
1587     krb5_ccache                         cc = 0;
1588     krb5_principal                      me = 0;
1589     krb5_principal              server = 0;
1590     krb5_creds                          my_creds;
1591     krb5_data                   *realm = 0;
1592
1593     if (!pkrb5_init_context)
1594         return 0;
1595
1596         memset(&my_creds, 0, sizeof(krb5_creds));
1597
1598     if ( alt_ctx ) {
1599         ctx = alt_ctx;
1600     } else {
1601         code = pkrb5_init_context(&ctx);
1602         if (code) goto cleanup;
1603     }
1604
1605     if ( alt_cc ) {
1606         cc = alt_cc;
1607     } else {
1608         code = pkrb5_cc_default(ctx, &cc);
1609         if (code) goto cleanup;
1610     }
1611
1612     code = pkrb5_cc_get_principal(ctx, cc, &me);
1613     if (code) goto cleanup;
1614
1615     realm = krb5_princ_realm(ctx, me);
1616
1617     code = pkrb5_build_principal_ext(ctx, &server,
1618                                     realm->length,realm->data,
1619                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
1620                                     realm->length,realm->data,
1621                                     0);
1622     if ( code ) 
1623         goto cleanup;
1624
1625     if ( IsDebuggerPresent() ) {
1626         char * cname, *sname;
1627         pkrb5_unparse_name(ctx, me, &cname);
1628         pkrb5_unparse_name(ctx, server, &sname);
1629         OutputDebugString("Renewing credential for client \"");
1630         OutputDebugString(cname);
1631         OutputDebugString("\" and service \"");
1632         OutputDebugString(sname);
1633         OutputDebugString("\"\n");
1634         pkrb5_free_unparsed_name(ctx,cname);
1635         pkrb5_free_unparsed_name(ctx,sname);
1636     }
1637
1638     my_creds.client = me;
1639     my_creds.server = server;
1640
1641     code = pkrb5_get_renewed_creds(ctx, &my_creds, me, cc, NULL);
1642     if (code) {
1643         if ( IsDebuggerPresent() ) {
1644             char message[256];
1645             sprintf(message,"krb5_get_renewed_creds() failed: %d\n",code);
1646             OutputDebugString(message);
1647         }
1648         goto cleanup;
1649     }
1650
1651     code = pkrb5_cc_initialize(ctx, cc, me);
1652     if (code) {
1653         if ( IsDebuggerPresent() ) {
1654             char message[256];
1655             sprintf(message,"krb5_cc_initialize() failed: %d\n",code);
1656             OutputDebugString(message);
1657         }
1658         goto cleanup;
1659     }
1660
1661     code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
1662     if (code) {
1663         if ( IsDebuggerPresent() ) {
1664             char message[256];
1665             sprintf(message,"krb5_cc_store_cred() failed: %d\n",code);
1666             OutputDebugString(message);
1667         }
1668         goto cleanup;
1669     }
1670
1671   cleanup:
1672     if (my_creds.client == me)
1673         my_creds.client = 0;
1674     if (my_creds.server == server)
1675         my_creds.server = 0;
1676     pkrb5_free_cred_contents(ctx, &my_creds);
1677     if (me)
1678         pkrb5_free_principal(ctx, me);
1679     if (server)
1680         pkrb5_free_principal(ctx, server);
1681     if (cc && (cc != alt_cc))
1682         pkrb5_cc_close(ctx, cc);
1683     if (ctx && (ctx != alt_ctx))
1684         pkrb5_free_context(ctx);
1685     return(code);
1686 }
1687
1688 int
1689 KFW_kinit( krb5_context alt_ctx,
1690             krb5_ccache  alt_cc,
1691             HWND hParent,
1692             char *principal_name,
1693             char *password,
1694             krb5_deltat lifetime,
1695             DWORD                       forwardable,
1696             DWORD                       proxiable,
1697             krb5_deltat                 renew_life,
1698             DWORD                       addressless,
1699             DWORD                       publicIP
1700             )
1701 {
1702     krb5_error_code                     code = 0;
1703     krb5_context                        ctx = 0;
1704     krb5_ccache                         cc = 0;
1705     krb5_principal                      me = 0;
1706     char*                       name = 0;
1707     krb5_creds                          my_creds;
1708     krb5_get_init_creds_opt     options;
1709     krb5_address **             addrs = NULL;
1710     int                         i = 0, addr_count = 0;
1711
1712     if (!pkrb5_init_context)
1713         return 0;
1714
1715     pkrb5_get_init_creds_opt_init(&options);
1716     memset(&my_creds, 0, sizeof(my_creds));
1717
1718     if (alt_ctx)
1719     {
1720         ctx = alt_ctx;
1721     }
1722     else
1723     {
1724         code = pkrb5_init_context(&ctx);
1725         if (code) goto cleanup;
1726     }
1727
1728     if ( alt_cc ) {
1729         cc = alt_cc;
1730     } else {
1731         code = pkrb5_cc_default(ctx, &cc);  
1732         if (code) goto cleanup;
1733     }
1734
1735     code = pkrb5_parse_name(ctx, principal_name, &me);
1736     if (code) 
1737                 goto cleanup;
1738
1739     code = pkrb5_unparse_name(ctx, me, &name);
1740     if (code) 
1741                 goto cleanup;
1742
1743     if (lifetime == 0)
1744         lifetime = pLeash_get_default_lifetime();
1745     else
1746         lifetime *= 5*60;
1747
1748         if (renew_life > 0)
1749                 renew_life *= 5*60;
1750
1751     if (lifetime)
1752         pkrb5_get_init_creds_opt_set_tkt_life(&options, lifetime);
1753         pkrb5_get_init_creds_opt_set_forwardable(&options,
1754                                                  forwardable ? 1 : 0);
1755         pkrb5_get_init_creds_opt_set_proxiable(&options,
1756                                                proxiable ? 1 : 0);
1757         pkrb5_get_init_creds_opt_set_renew_life(&options,
1758                                                renew_life);
1759     if (addressless)
1760         pkrb5_get_init_creds_opt_set_address_list(&options,NULL);
1761     else {
1762                 if (publicIP)
1763         {
1764             // we are going to add the public IP address specified by the user
1765             // to the list provided by the operating system
1766             krb5_address ** local_addrs=NULL;
1767             DWORD           netIPAddr;
1768
1769             pkrb5_os_localaddr(ctx, &local_addrs);
1770             while ( local_addrs[i++] );
1771             addr_count = i + 1;
1772
1773             addrs = (krb5_address **) malloc((addr_count+1) * sizeof(krb5_address *));
1774             if ( !addrs ) {
1775                 pkrb5_free_addresses(ctx, local_addrs);
1776                 goto cleanup;
1777             }
1778             memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1));
1779             i = 0;
1780             while ( local_addrs[i] ) {
1781                 addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
1782                 if (addrs[i] == NULL) {
1783                     pkrb5_free_addresses(ctx, local_addrs);
1784                     goto cleanup;
1785                 }
1786
1787                 addrs[i]->magic = local_addrs[i]->magic;
1788                 addrs[i]->addrtype = local_addrs[i]->addrtype;
1789                 addrs[i]->length = local_addrs[i]->length;
1790                 addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
1791                 if (!addrs[i]->contents) {
1792                     pkrb5_free_addresses(ctx, local_addrs);
1793                     goto cleanup;
1794                 }
1795
1796                 memcpy(addrs[i]->contents,local_addrs[i]->contents,
1797                         local_addrs[i]->length);        /* safe */
1798                 i++;
1799             }
1800             pkrb5_free_addresses(ctx, local_addrs);
1801
1802             addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
1803             if (addrs[i] == NULL)
1804                 goto cleanup;
1805
1806             addrs[i]->magic = KV5M_ADDRESS;
1807             addrs[i]->addrtype = AF_INET;
1808             addrs[i]->length = 4;
1809             addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
1810             if (!addrs[i]->contents)
1811                 goto cleanup;
1812
1813             netIPAddr = htonl(publicIP);
1814             memcpy(addrs[i]->contents,&netIPAddr,4);
1815         
1816             pkrb5_get_init_creds_opt_set_address_list(&options,addrs);
1817
1818         }
1819     }
1820
1821     code = pkrb5_get_init_creds_password(ctx, 
1822                                        &my_creds, 
1823                                        me,
1824                                        password, // password
1825                                        KRB5_prompter, // prompter
1826                                        hParent, // prompter data
1827                                        0, // start time
1828                                        0, // service name
1829                                        &options);
1830     if (code) 
1831                 goto cleanup;
1832
1833     code = pkrb5_cc_initialize(ctx, cc, me);
1834     if (code) 
1835                 goto cleanup;
1836
1837     code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
1838     if (code) 
1839                 goto cleanup;
1840
1841  cleanup:
1842     if ( addrs ) {
1843         for ( i=0;i<addr_count;i++ ) {
1844             if ( addrs[i] ) {
1845                 if ( addrs[i]->contents )
1846                     free(addrs[i]->contents);
1847                 free(addrs[i]);
1848             }
1849         }
1850     }
1851     if (my_creds.client == me)
1852         my_creds.client = 0;
1853     pkrb5_free_cred_contents(ctx, &my_creds);
1854     if (name)
1855         pkrb5_free_unparsed_name(ctx, name);
1856     if (me)
1857         pkrb5_free_principal(ctx, me);
1858     if (cc && (cc != alt_cc))
1859         pkrb5_cc_close(ctx, cc);
1860     if (ctx && (ctx != alt_ctx))
1861         pkrb5_free_context(ctx);
1862     return(code);
1863 }
1864
1865
1866 int
1867 KFW_kdestroy(krb5_context alt_ctx, krb5_ccache alt_cc)
1868 {
1869     krb5_context                ctx;
1870     krb5_ccache                 cc;
1871     krb5_error_code             code;
1872
1873     if (!pkrb5_init_context)
1874         return 0;
1875
1876     if (alt_ctx)
1877     {
1878         ctx = alt_ctx;
1879     }
1880     else
1881     {
1882         code = pkrb5_init_context(&ctx);
1883         if (code) goto cleanup;
1884     }
1885
1886     if ( alt_cc ) {
1887         cc = alt_cc;
1888     } else {
1889         code = pkrb5_cc_default(ctx, &cc);  
1890         if (code) goto cleanup;
1891     }
1892
1893     code = pkrb5_cc_destroy(ctx, cc);
1894     if ( !code ) cc = 0;
1895
1896   cleanup:
1897     if (cc && (cc != alt_cc))
1898         pkrb5_cc_close(ctx, cc);
1899     if (ctx && (ctx != alt_ctx))
1900         pkrb5_free_context(ctx);
1901
1902     return(code);
1903 }
1904
1905
1906 #ifdef USE_MS2MIT
1907 static BOOL
1908 GetSecurityLogonSessionData(PSECURITY_LOGON_SESSION_DATA * ppSessionData)
1909 {
1910     NTSTATUS Status = 0;
1911     HANDLE  TokenHandle;
1912     TOKEN_STATISTICS Stats;
1913     DWORD   ReqLen;
1914     BOOL    Success;
1915
1916     if (!ppSessionData)
1917         return FALSE;
1918     *ppSessionData = NULL;
1919
1920     Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle );
1921     if ( !Success )
1922         return FALSE;
1923
1924     Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );
1925     CloseHandle( TokenHandle );
1926     if ( !Success )
1927         return FALSE;
1928
1929     Status = pLsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );
1930     if ( FAILED(Status) || !ppSessionData )
1931         return FALSE;
1932
1933     return TRUE;
1934 }
1935
1936 //
1937 // MSLSA_IsKerberosLogon() does not validate whether or not there are valid tickets in the 
1938 // cache.  It validates whether or not it is reasonable to assume that if we 
1939 // attempted to retrieve valid tickets we could do so.  Microsoft does not 
1940 // automatically renew expired tickets.  Therefore, the cache could contain
1941 // expired or invalid tickets.  Microsoft also caches the user's password 
1942 // and will use it to retrieve new TGTs if the cache is empty and tickets
1943 // are requested.
1944
1945 static BOOL
1946 MSLSA_IsKerberosLogon(VOID)
1947 {
1948     PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
1949     BOOL    Success = FALSE;
1950
1951     if ( GetSecurityLogonSessionData(&pSessionData) ) {
1952         if ( pSessionData->AuthenticationPackage.Buffer ) {
1953             WCHAR buffer[256];
1954             WCHAR *usBuffer;
1955             int usLength;
1956
1957             Success = FALSE;
1958             usBuffer = (pSessionData->AuthenticationPackage).Buffer;
1959             usLength = (pSessionData->AuthenticationPackage).Length;
1960             if (usLength < 256)
1961             {
1962                 lstrcpynW (buffer, usBuffer, usLength);
1963                 lstrcatW (buffer,L"");
1964                 if ( !lstrcmpW(L"Kerberos",buffer) )
1965                     Success = TRUE;
1966             }
1967         }
1968         pLsaFreeReturnBuffer(pSessionData);
1969     }
1970     return Success;
1971 }
1972 #endif /* USE_MS2MIT */
1973
1974 static BOOL CALLBACK 
1975 MultiInputDialogProc( HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam)
1976 {
1977     int i;
1978
1979     switch ( message ) {
1980     case WM_INITDIALOG:
1981         if ( GetDlgCtrlID((HWND) wParam) != ID_MID_TEXT )
1982         {
1983             SetFocus(GetDlgItem( hDialog, ID_MID_TEXT));
1984             return FALSE;
1985         }
1986                 for ( i=0; i < mid_cnt ; i++ ) {
1987                         if (mid_tb[i].echo == 0)
1988                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, 32, 0);
1989                     else if (mid_tb[i].echo == 2) 
1990                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, '*', 0);
1991                 }
1992         return TRUE;
1993
1994     case WM_COMMAND:
1995         switch ( LOWORD(wParam) ) {
1996         case IDOK:
1997             for ( i=0; i < mid_cnt ; i++ ) {
1998                 if ( !GetDlgItemText(hDialog, ID_MID_TEXT+i, mid_tb[i].buf, mid_tb[i].len) )
1999                     *mid_tb[i].buf = '\0';
2000             }
2001             /* fallthrough */
2002         case IDCANCEL:
2003             EndDialog(hDialog, LOWORD(wParam));
2004             return TRUE;
2005         }
2006     }
2007     return FALSE;
2008 }
2009
2010 static LPWORD 
2011 lpwAlign( LPWORD lpIn )
2012 {
2013     ULONG ul;
2014
2015     ul = (ULONG) lpIn;
2016     ul += 3;
2017     ul >>=2;
2018     ul <<=2;
2019     return (LPWORD) ul;;
2020 }
2021
2022 /*
2023  * dialog widths are measured in 1/4 character widths
2024  * dialog height are measured in 1/8 character heights
2025  */
2026
2027 static LRESULT
2028 MultiInputDialog( HINSTANCE hinst, HWND hwndOwner, 
2029                   char * ptext[], int numlines, int width, 
2030                   int tb_cnt, struct textField * tb)
2031 {
2032     HGLOBAL hgbl;
2033     LPDLGTEMPLATE lpdt;
2034     LPDLGITEMTEMPLATE lpdit;
2035     LPWORD lpw;
2036     LPWSTR lpwsz;
2037     LRESULT ret;
2038     int nchar, i, pwid;
2039
2040     hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
2041     if (!hgbl)
2042         return -1;
2043  
2044     mid_cnt = tb_cnt;
2045     mid_tb = tb;
2046
2047     lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
2048  
2049     // Define a dialog box.
2050  
2051     lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU
2052                    | DS_MODALFRAME | WS_CAPTION | DS_CENTER 
2053                    | DS_SETFOREGROUND | DS_3DLOOK
2054                    | DS_SETFONT | DS_FIXEDSYS | DS_NOFAILCREATE;
2055     lpdt->cdit = numlines + (2 * tb_cnt) + 2;  // number of controls
2056     lpdt->x  = 10;  
2057     lpdt->y  = 10;
2058     lpdt->cx = 20 + width * 4; 
2059     lpdt->cy = 20 + (numlines + tb_cnt + 4) * 14;
2060
2061     lpw = (LPWORD) (lpdt + 1);
2062     *lpw++ = 0;   // no menu
2063     *lpw++ = 0;   // predefined dialog box class (by default)
2064
2065     lpwsz = (LPWSTR) lpw;
2066     nchar = MultiByteToWideChar (CP_ACP, 0, "", -1, lpwsz, 128);
2067     lpw   += nchar;
2068     *lpw++ = 8;                        // font size (points)
2069     lpwsz = (LPWSTR) lpw;
2070     nchar = MultiByteToWideChar (CP_ACP, 0, "MS Shell Dlg", 
2071                                     -1, lpwsz, 128);
2072     lpw   += nchar;
2073
2074     //-----------------------
2075     // Define an OK button.
2076     //-----------------------
2077     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
2078     lpdit = (LPDLGITEMTEMPLATE) lpw;
2079     lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP | WS_BORDER;
2080     lpdit->dwExtendedStyle = 0;
2081     lpdit->x  = (lpdt->cx - 14)/4 - 20; 
2082     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
2083     lpdit->cx = 40; 
2084     lpdit->cy = 14;
2085     lpdit->id = IDOK;  // OK button identifier
2086
2087     lpw = (LPWORD) (lpdit + 1);
2088     *lpw++ = 0xFFFF;
2089     *lpw++ = 0x0080;    // button class
2090
2091     lpwsz = (LPWSTR) lpw;
2092     nchar = MultiByteToWideChar (CP_ACP, 0, "OK", -1, lpwsz, 50);
2093     lpw   += nchar;
2094     *lpw++ = 0;           // no creation data
2095
2096     //-----------------------
2097     // Define an Cancel button.
2098     //-----------------------
2099     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
2100     lpdit = (LPDLGITEMTEMPLATE) lpw;
2101     lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP | WS_BORDER;
2102     lpdit->dwExtendedStyle = 0;
2103     lpdit->x  = (lpdt->cx - 14)*3/4 - 20; 
2104     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
2105     lpdit->cx = 40; 
2106     lpdit->cy = 14;
2107     lpdit->id = IDCANCEL;  // CANCEL button identifier
2108
2109     lpw = (LPWORD) (lpdit + 1);
2110     *lpw++ = 0xFFFF;
2111     *lpw++ = 0x0080;    // button class
2112
2113     lpwsz = (LPWSTR) lpw;
2114     nchar = MultiByteToWideChar (CP_ACP, 0, "Cancel", -1, lpwsz, 50);
2115     lpw   += nchar;
2116     *lpw++ = 0;           // no creation data
2117
2118     /* Add controls for preface data */
2119     for ( i=0; i<numlines; i++) {
2120         /*-----------------------
2121          * Define a static text control.
2122          *-----------------------*/
2123         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
2124         lpdit = (LPDLGITEMTEMPLATE) lpw;
2125         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
2126         lpdit->dwExtendedStyle = 0;
2127         lpdit->x  = 10; 
2128         lpdit->y  = 10 + i * 14;
2129         lpdit->cx = strlen(ptext[i]) * 4 + 10; 
2130         lpdit->cy = 14;
2131         lpdit->id = ID_TEXT + i;  // text identifier
2132
2133         lpw = (LPWORD) (lpdit + 1);
2134         *lpw++ = 0xFFFF;
2135         *lpw++ = 0x0082;                         // static class
2136
2137         lpwsz = (LPWSTR) lpw;
2138         nchar = MultiByteToWideChar (CP_ACP, 0, ptext[i], 
2139                                          -1, lpwsz, 2*width);
2140         lpw   += nchar;
2141         *lpw++ = 0;           // no creation data
2142     }
2143     
2144     for ( i=0, pwid = 0; i<tb_cnt; i++) {
2145         if ( pwid < strlen(tb[i].label) )
2146             pwid = strlen(tb[i].label);
2147     }
2148
2149     for ( i=0; i<tb_cnt; i++) {
2150         /* Prompt */
2151         /*-----------------------
2152          * Define a static text control.
2153          *-----------------------*/
2154         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
2155         lpdit = (LPDLGITEMTEMPLATE) lpw;
2156         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
2157         lpdit->dwExtendedStyle = 0;
2158         lpdit->x  = 10; 
2159         lpdit->y  = 10 + (numlines + i + 1) * 14;
2160         lpdit->cx = pwid * 4; 
2161         lpdit->cy = 14;
2162         lpdit->id = ID_TEXT + numlines + i;  // text identifier
2163
2164         lpw = (LPWORD) (lpdit + 1);
2165         *lpw++ = 0xFFFF;
2166         *lpw++ = 0x0082;                         // static class
2167
2168         lpwsz = (LPWSTR) lpw;
2169         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].label ? tb[i].label : "", 
2170                                      -1, lpwsz, 128);
2171         lpw   += nchar;
2172         *lpw++ = 0;           // no creation data
2173
2174         /*-----------------------
2175          * Define an edit control.
2176          *-----------------------*/
2177         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
2178         lpdit = (LPDLGITEMTEMPLATE) lpw;
2179         lpdit->style = WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | WS_BORDER | (tb[i].echo == 1 ? 0L : ES_PASSWORD);
2180         lpdit->dwExtendedStyle = 0;
2181         lpdit->x  = 10 + (pwid + 1) * 4; 
2182         lpdit->y  = 10 + (numlines + i + 1) * 14;
2183         lpdit->cx = (width - (pwid + 1)) * 4; 
2184         lpdit->cy = 14;
2185         lpdit->id = ID_MID_TEXT + i;             // identifier
2186
2187         lpw = (LPWORD) (lpdit + 1);
2188         *lpw++ = 0xFFFF;
2189         *lpw++ = 0x0081;                         // edit class
2190
2191         lpwsz = (LPWSTR) lpw;
2192         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].def ? tb[i].def : "", 
2193                                      -1, lpwsz, 128);
2194         lpw   += nchar;
2195         *lpw++ = 0;           // no creation data
2196     }
2197
2198     GlobalUnlock(hgbl); 
2199     ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl, 
2200                                                         hwndOwner, (DLGPROC) MultiInputDialogProc); 
2201     GlobalFree(hgbl); 
2202
2203     switch ( ret ) {
2204     case 0:     /* Timeout */
2205         return -1;
2206     case IDOK:
2207         return 1;
2208     case IDCANCEL:
2209         return 0;
2210     default: {
2211         char buf[256];
2212         sprintf(buf,"DialogBoxIndirect() failed: %d",GetLastError());
2213         MessageBox(hwndOwner,
2214                     buf,
2215                     "GetLastError()",
2216                     MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
2217         return -1;
2218     }
2219     }
2220 }
2221
2222 static int
2223 multi_field_dialog(HWND hParent, char * preface, int n, struct textField tb[])
2224 {
2225         HINSTANCE hInst = 0;
2226     int maxwidth = 0;
2227     int numlines = 0;
2228     int len;
2229     char * plines[16], *p = preface ? preface : "";
2230     int i;
2231
2232     for ( i=0; i<16; i++ ) 
2233         plines[i] = NULL;
2234
2235     while (*p && numlines < 16) {
2236         plines[numlines++] = p;
2237         for ( ;*p && *p != '\r' && *p != '\n'; p++ );
2238         if ( *p == '\r' && *(p+1) == '\n' ) {
2239             *p++ = '\0';
2240             p++;
2241         } else if ( *p == '\n' ) {
2242             *p++ = '\0';
2243         } 
2244         if ( strlen(plines[numlines-1]) > maxwidth )
2245             maxwidth = strlen(plines[numlines-1]);
2246     }
2247
2248     for ( i=0;i<n;i++ ) {
2249         len = strlen(tb[i].label) + 1 + (tb[i].len > 40 ? 40 : tb[i].len);
2250         if ( maxwidth < len )
2251             maxwidth = len;
2252     }
2253
2254     return(MultiInputDialog(hInst, hParent, plines, numlines, maxwidth, n, tb));
2255 }
2256
2257 static krb5_error_code KRB5_CALLCONV
2258 KRB5_prompter( krb5_context context,
2259                void *data,
2260                const char *name,
2261                const char *banner,
2262                int num_prompts,
2263                krb5_prompt prompts[])
2264 {
2265     krb5_error_code     errcode = 0;
2266     int                 i;
2267     struct textField * tb = NULL;
2268     int    len = 0, blen=0, nlen=0;
2269         HWND hParent = (HWND)data;
2270
2271     if (name)
2272         nlen = strlen(name)+2;
2273
2274     if (banner)
2275         blen = strlen(banner)+2;
2276
2277     tb = (struct textField *) malloc(sizeof(struct textField) * num_prompts);
2278     if ( tb != NULL ) {
2279         int ok;
2280         memset(tb,0,sizeof(struct textField) * num_prompts);
2281         for ( i=0; i < num_prompts; i++ ) {
2282             tb[i].buf = prompts[i].reply->data;
2283             tb[i].len = prompts[i].reply->length;
2284             tb[i].label = prompts[i].prompt;
2285             tb[i].def = NULL;
2286             tb[i].echo = (prompts[i].hidden ? 2 : 1);
2287         }   
2288
2289         ok = multi_field_dialog(hParent,(char *)banner,num_prompts,tb);
2290         if ( ok ) {
2291             for ( i=0; i < num_prompts; i++ )
2292                 prompts[i].reply->length = strlen(prompts[i].reply->data);
2293         } else
2294             errcode = -2;
2295     }
2296
2297     if ( tb )
2298         free(tb);
2299     if (errcode) {
2300         for (i = 0; i < num_prompts; i++) {
2301             memset(prompts[i].reply->data, 0, prompts[i].reply->length);
2302         }
2303     }
2304     return errcode;
2305 }
2306
2307 BOOL
2308 KFW_AFS_wait_for_service_start(void)
2309 {
2310     char    HostName[64];
2311     DWORD   CurrentState;
2312
2313     CurrentState = SERVICE_START_PENDING;
2314     memset(HostName, '\0', sizeof(HostName));
2315     gethostname(HostName, sizeof(HostName));
2316
2317     while (CurrentState != SERVICE_RUNNING || CurrentState != SERVICE_STOPPED)
2318     {
2319         if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
2320             return(0);
2321         if ( IsDebuggerPresent() ) {
2322             switch ( CurrentState ) {
2323             case SERVICE_STOPPED:
2324                 OutputDebugString("SERVICE_STOPPED\n");
2325                 break;
2326             case SERVICE_START_PENDING:
2327                 OutputDebugString("SERVICE_START_PENDING\n");
2328                 break;
2329             case SERVICE_STOP_PENDING:
2330                 OutputDebugString("SERVICE_STOP_PENDING\n");
2331                 break;
2332             case SERVICE_RUNNING:
2333                 OutputDebugString("SERVICE_RUNNING\n");
2334                 break;
2335             case SERVICE_CONTINUE_PENDING:
2336                 OutputDebugString("SERVICE_CONTINUE_PENDING\n");
2337                 break;
2338             case SERVICE_PAUSE_PENDING:
2339                 OutputDebugString("SERVICE_PAUSE_PENDING\n");
2340                 break;
2341             case SERVICE_PAUSED:
2342                 OutputDebugString("SERVICE_PAUSED\n");
2343                 break;
2344             default:
2345                 OutputDebugString("UNKNOWN Service State\n");
2346             }
2347         }
2348         if (CurrentState == SERVICE_STOPPED)
2349             return(0);
2350         if (CurrentState == SERVICE_RUNNING)
2351             return(1);
2352         Sleep(500);
2353     }
2354     return(0);
2355 }
2356
2357
2358 int
2359 KFW_AFS_unlog(void)
2360 {
2361     long        rc;
2362     char    HostName[64];
2363     DWORD   CurrentState;
2364
2365     CurrentState = 0;
2366     memset(HostName, '\0', sizeof(HostName));
2367     gethostname(HostName, sizeof(HostName));
2368     if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
2369         return(0);
2370     if (CurrentState != SERVICE_RUNNING)
2371         return(0);
2372
2373     rc = pktc_ForgetAllTokens();
2374
2375     return(0);
2376 }
2377
2378
2379 #define TKTLIFENUMFIXED 64
2380 #define TKTLIFEMINFIXED 0x80
2381 #define TKTLIFEMAXFIXED 0xBF
2382 #define TKTLIFENOEXPIRE 0xFF
2383 #define MAXTKTLIFETIME  (30*24*3600)    /* 30 days */
2384 #ifndef NEVERDATE
2385 #define NEVERDATE ((unsigned long)0x7fffffffL)
2386 #endif
2387
2388 static int no_long_lifetimes = 0;
2389 typedef unsigned long u_int32_t;
2390
2391 static const int tkt_lifetimes[TKTLIFENUMFIXED] = {
2392     38400,                              /* 10.67 hours, 0.44 days */ 
2393     41055,                              /* 11.40 hours, 0.48 days */ 
2394     43894,                              /* 12.19 hours, 0.51 days */ 
2395     46929,                              /* 13.04 hours, 0.54 days */ 
2396     50174,                              /* 13.94 hours, 0.58 days */ 
2397     53643,                              /* 14.90 hours, 0.62 days */ 
2398     57352,                              /* 15.93 hours, 0.66 days */ 
2399     61318,                              /* 17.03 hours, 0.71 days */ 
2400     65558,                              /* 18.21 hours, 0.76 days */ 
2401     70091,                              /* 19.47 hours, 0.81 days */ 
2402     74937,                              /* 20.82 hours, 0.87 days */ 
2403     80119,                              /* 22.26 hours, 0.93 days */ 
2404     85658,                              /* 23.79 hours, 0.99 days */ 
2405     91581,                              /* 25.44 hours, 1.06 days */ 
2406     97914,                              /* 27.20 hours, 1.13 days */ 
2407     104684,                             /* 29.08 hours, 1.21 days */ 
2408     111922,                             /* 31.09 hours, 1.30 days */ 
2409     119661,                             /* 33.24 hours, 1.38 days */ 
2410     127935,                             /* 35.54 hours, 1.48 days */ 
2411     136781,                             /* 37.99 hours, 1.58 days */ 
2412     146239,                             /* 40.62 hours, 1.69 days */ 
2413     156350,                             /* 43.43 hours, 1.81 days */ 
2414     167161,                             /* 46.43 hours, 1.93 days */ 
2415     178720,                             /* 49.64 hours, 2.07 days */ 
2416     191077,                             /* 53.08 hours, 2.21 days */ 
2417     204289,                             /* 56.75 hours, 2.36 days */ 
2418     218415,                             /* 60.67 hours, 2.53 days */ 
2419     233517,                             /* 64.87 hours, 2.70 days */ 
2420     249664,                             /* 69.35 hours, 2.89 days */ 
2421     266926,                             /* 74.15 hours, 3.09 days */ 
2422     285383,                             /* 79.27 hours, 3.30 days */ 
2423     305116,                             /* 84.75 hours, 3.53 days */ 
2424     326213,                             /* 90.61 hours, 3.78 days */ 
2425     348769,                             /* 96.88 hours, 4.04 days */ 
2426     372885,                             /* 103.58 hours, 4.32 days */ 
2427     398668,                             /* 110.74 hours, 4.61 days */ 
2428     426234,                             /* 118.40 hours, 4.93 days */ 
2429     455705,                             /* 126.58 hours, 5.27 days */ 
2430     487215,                             /* 135.34 hours, 5.64 days */ 
2431     520904,                             /* 144.70 hours, 6.03 days */ 
2432     556921,                             /* 154.70 hours, 6.45 days */ 
2433     595430,                             /* 165.40 hours, 6.89 days */ 
2434     636601,                             /* 176.83 hours, 7.37 days */ 
2435     680618,                             /* 189.06 hours, 7.88 days */ 
2436     727680,                             /* 202.13 hours, 8.42 days */ 
2437     777995,                             /* 216.11 hours, 9.00 days */ 
2438     831789,                             /* 231.05 hours, 9.63 days */ 
2439     889303,                             /* 247.03 hours, 10.29 days */
2440
2441     950794,                             /* 264.11 hours, 11.00 days */
2442
2443     1016537,                            /* 282.37 hours, 11.77 days */
2444
2445     1086825,                            /* 301.90 hours, 12.58 days */
2446
2447     1161973,                            /* 322.77 hours, 13.45 days */
2448
2449     1242318,                            /* 345.09 hours, 14.38 days */
2450
2451     1328218,                            /* 368.95 hours, 15.37 days */
2452
2453     1420057,                            /* 394.46 hours, 16.44 days */
2454
2455     1518247,                            /* 421.74 hours, 17.57 days */
2456
2457     1623226,                            /* 450.90 hours, 18.79 days */
2458
2459     1735464,                            /* 482.07 hours, 20.09 days */
2460
2461     1855462,                            /* 515.41 hours, 21.48 days */
2462
2463     1983758,                            /* 551.04 hours, 22.96 days */
2464
2465     2120925,                            /* 589.15 hours, 24.55 days */
2466
2467     2267576,                            /* 629.88 hours, 26.25 days */
2468
2469     2424367,                            /* 673.44 hours, 28.06 days */
2470
2471     2592000};                           /* 720.00 hours, 30.00 days */
2472
2473
2474 /*
2475  * KFW_KRB4_life_to_time - takes a start time and a Kerberos standard
2476  * lifetime char and returns the corresponding end time.  There are
2477  * four simple cases to be handled.  The first is a life of 0xff,
2478  * meaning no expiration, and results in an end time of 0xffffffff.
2479  * The second is when life is less than the values covered by the
2480  * table.  In this case, the end time is the start time plus the
2481  * number of 5 minute intervals specified by life.  The third case
2482  * returns start plus the MAXTKTLIFETIME if life is greater than
2483  * TKTLIFEMAXFIXED.  The last case, uses the life value (minus
2484  * TKTLIFEMINFIXED) as an index into the table to extract the lifetime
2485  * in seconds, which is added to start to produce the end time.
2486  */
2487 static u_int32_t
2488 KFW_KRB4_life_to_time(u_int32_t start, int life_)
2489 {
2490     unsigned char life = (unsigned char) life_;
2491
2492     if (no_long_lifetimes) return start + life*5*60;
2493
2494     if (life == TKTLIFENOEXPIRE) return NEVERDATE;
2495     if (life < TKTLIFEMINFIXED) return start + life*5*60;
2496     if (life > TKTLIFEMAXFIXED) return start + MAXTKTLIFETIME;
2497     return start + tkt_lifetimes[life - TKTLIFEMINFIXED];
2498 }
2499
2500 /*
2501  * KFW_KRB4_time_to_life - takes start and end times for the ticket and
2502  * returns a Kerberos standard lifetime char, possibily using the
2503  * tkt_lifetimes table for lifetimes above 127*5 minutes.  First, the
2504  * special case of (end == NEVERDATE) is handled to mean no
2505  * expiration.  Then negative lifetimes and those greater than the
2506  * maximum ticket lifetime are rejected.  Then lifetimes less than the
2507  * first table entry are handled by rounding the requested lifetime
2508  * *up* to the next 5 minute interval.  The final step is to search
2509  * the table for the smallest entry *greater than or equal* to the
2510  * requested entry.
2511  */
2512 static int 
2513 KFW_KRB4_time_to_life(u_int32_t start, u_int32_t end)
2514 {
2515     int i;
2516     long lifetime = end - start;
2517
2518     if (no_long_lifetimes) return (lifetime + 5*60 - 1)/(5*60);
2519
2520     if (end >= NEVERDATE) return TKTLIFENOEXPIRE;
2521     if (lifetime > MAXTKTLIFETIME || lifetime <= 0) return 0;
2522     if (lifetime < tkt_lifetimes[0]) return (lifetime + 5*60 - 1)/(5*60);
2523     for (i=0; i<TKTLIFENUMFIXED; i++) {
2524         if (lifetime <= tkt_lifetimes[i]) {
2525             return i+TKTLIFEMINFIXED;
2526         }
2527     }
2528     return 0;
2529 }
2530
2531
2532 int
2533 KFW_AFS_klog(
2534     krb5_context alt_ctx,
2535     krb5_ccache  alt_cc,
2536     char *service,
2537     char *cell,
2538     char *realm,
2539     int LifeTime
2540     )
2541 {
2542     long        rc = 0;
2543     CREDENTIALS creds;
2544     KTEXT_ST    ticket;
2545     struct ktc_principal        aserver;
2546     struct ktc_principal        aclient;
2547     char        username[BUFSIZ];       /* To hold client username structure */
2548     char        realm_of_user[REALM_SZ]; /* Kerberos realm of user */
2549     char        realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
2550     char        local_cell[MAXCELLCHARS+1];
2551     char        Dmycell[MAXCELLCHARS+1];
2552     struct ktc_token    atoken;
2553     struct ktc_token    btoken;
2554     afsconf_cell        ak_cellconfig; /* General information about the cell */
2555     char        RealmName[128];
2556     char        CellName[128];
2557     char        ServiceName[128];
2558     DWORD       CurrentState;
2559     char        HostName[64];
2560     BOOL        try_krb5 = 0;
2561     krb5_context  ctx = 0;
2562     krb5_ccache   cc = 0;
2563     krb5_creds increds;
2564     krb5_creds * k5creds = 0;
2565     krb5_error_code code;
2566     krb5_principal client_principal = 0;
2567     int i, retry = 0;
2568
2569     CurrentState = 0;
2570     memset(HostName, '\0', sizeof(HostName));
2571     gethostname(HostName, sizeof(HostName));
2572     if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR) {
2573         if ( IsDebuggerPresent() )
2574             OutputDebugString("Unable to retrieve AFSD Service Status\n");
2575         return(-1);
2576     }
2577     if (CurrentState != SERVICE_RUNNING) {
2578         if ( IsDebuggerPresent() )
2579             OutputDebugString("AFSD Service NOT RUNNING\n");
2580         return(-2);
2581     }
2582
2583     if (!pkrb5_init_context)
2584         return 0;
2585
2586     memset(RealmName, '\0', sizeof(RealmName));
2587     memset(CellName, '\0', sizeof(CellName));
2588     memset(ServiceName, '\0', sizeof(ServiceName));
2589     memset(realm_of_user, '\0', sizeof(realm_of_user));
2590     memset(realm_of_cell, '\0', sizeof(realm_of_cell));
2591         if (cell && cell[0])
2592                 strcpy(Dmycell, cell);
2593         else
2594                 memset(Dmycell, '\0', sizeof(Dmycell));
2595
2596     // NULL or empty cell returns information on local cell
2597     if (rc = get_cellconfig(Dmycell, &ak_cellconfig, local_cell))
2598     {
2599                 KFW_AFS_error(rc, "get_cellconfig()");
2600         return(rc);
2601     }
2602
2603     if ( alt_ctx ) {
2604         ctx = alt_ctx;
2605     } else {
2606         code = pkrb5_init_context(&ctx);
2607         if (code) goto cleanup;
2608     }
2609
2610     if ( alt_cc ) {
2611         cc = alt_cc;
2612     } else {
2613         code = pkrb5_cc_default(ctx, &cc);
2614         if (code) goto skip_krb5_init;
2615     }
2616
2617     memset((char *)&increds, 0, sizeof(increds));
2618
2619     code = pkrb5_cc_get_principal(ctx, cc, &client_principal);
2620         if (code) {
2621         if ( code == KRB5_CC_NOTFOUND && IsDebuggerPresent() ) 
2622         {
2623             OutputDebugString("Principal Not Found for ccache\n");
2624         }
2625         goto skip_krb5_init;
2626     }
2627     i = krb5_princ_realm(ctx, client_principal)->length;
2628     if (i > REALM_SZ-1) 
2629         i = REALM_SZ-1;
2630     strncpy(realm_of_user,krb5_princ_realm(ctx, client_principal)->data,i);
2631     realm_of_user[i] = 0;
2632     try_krb5 = 1;
2633
2634   skip_krb5_init:
2635 #ifdef USE_KRB4
2636     if ( !try_krb5 || !realm_of_user[0] ) {
2637         if ((rc = (*pkrb_get_tf_realm)((*ptkt_string)(), realm_of_user)) != KSUCCESS)
2638         {
2639             goto cleanup;
2640         }       
2641     }
2642 #else
2643     goto cleanup;
2644 #endif
2645     strcpy(realm_of_cell, afs_realm_of_cell(&ak_cellconfig));
2646
2647     if (strlen(service) == 0)
2648         strcpy(ServiceName, "afs");
2649     else
2650         strcpy(ServiceName, service);
2651
2652     if (strlen(cell) == 0)
2653         strcpy(CellName, local_cell);
2654     else
2655         strcpy(CellName, cell);
2656
2657     if (strlen(realm) == 0)
2658         strcpy(RealmName, realm_of_cell);
2659     else
2660         strcpy(RealmName, realm);
2661
2662     memset(&creds, '\0', sizeof(creds));
2663
2664     if ( try_krb5 ) {
2665         /* First try service/cell@REALM */
2666         if (code = pkrb5_build_principal(ctx, &increds.server,
2667                                           strlen(RealmName),
2668                                           RealmName,
2669                                           ServiceName,
2670                                           CellName,
2671                                           0)) 
2672         {
2673             goto cleanup;
2674         }
2675
2676         increds.client = client_principal;
2677         increds.times.endtime = 0;
2678         /* Ask for DES since that is what V4 understands */
2679         increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
2680
2681         if ( IsDebuggerPresent() ) {
2682             char * cname, *sname;
2683             pkrb5_unparse_name(ctx, increds.client, &cname);
2684             pkrb5_unparse_name(ctx, increds.server, &sname);
2685             OutputDebugString("Getting tickets for \"");
2686             OutputDebugString(cname);
2687             OutputDebugString("\" and service \"");
2688             OutputDebugString(sname);
2689             OutputDebugString("\"\n");
2690             pkrb5_free_unparsed_name(ctx,cname);
2691             pkrb5_free_unparsed_name(ctx,sname);
2692         }
2693
2694         code = pkrb5_get_credentials(ctx, 0, cc, &increds, &k5creds);
2695         if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
2696              code == KRB5KRB_ERR_GENERIC /* heimdal */) {
2697             /* Or service@REALM */
2698             pkrb5_free_principal(ctx,increds.server);
2699             increds.server = 0;
2700             code = pkrb5_build_principal(ctx, &increds.server,
2701                                           strlen(RealmName),
2702                                           RealmName,
2703                                           ServiceName,
2704                                           0);
2705
2706             if ( IsDebuggerPresent() ) {
2707                 char * cname, *sname;
2708                 pkrb5_unparse_name(ctx, increds.client, &cname);
2709                 pkrb5_unparse_name(ctx, increds.server, &sname);
2710                 OutputDebugString("krb5_get_credentials() returned Service Principal Unknown\n");
2711                 OutputDebugString("Trying again: getting tickets for \"");
2712                 OutputDebugString(cname);
2713                 OutputDebugString("\" and service \"");
2714                 OutputDebugString(sname);
2715                 OutputDebugString("\"\n");
2716                 pkrb5_free_unparsed_name(ctx,cname);
2717                 pkrb5_free_unparsed_name(ctx,sname);
2718             }
2719
2720             if (!code)
2721                 code = pkrb5_get_credentials(ctx, 0, cc, &increds, &k5creds);
2722         }
2723
2724         if (code) {
2725             if ( IsDebuggerPresent() ) {
2726                 char message[256];
2727                 sprintf(message,"krb5_get_credentials returns: %d\n",code);
2728                 OutputDebugString(message);
2729             }
2730             try_krb5 = 0;
2731             goto use_krb4;
2732         }
2733         /* This requires krb524d to be running with the KDC */
2734         code = pkrb524_convert_creds_kdc(ctx, k5creds, &creds);
2735         pkrb5_free_creds(ctx, k5creds);
2736         if (code) {
2737             if ( IsDebuggerPresent() ) {
2738                 char message[256];
2739                 sprintf(message,"krb524_convert_creds_kdc returns: %d\n",code);
2740                 OutputDebugString(message);
2741             }
2742             try_krb5 = 0;
2743             goto use_krb4;
2744         }
2745     } else {
2746       use_krb4:
2747 #ifdef USE_KRB4
2748         rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds);
2749         if (rc == NO_TKT_FIL) {
2750             // if the problem is that we have no krb4 tickets
2751             // do not attempt to continue
2752             goto cleanup;
2753         }
2754         if (rc != KSUCCESS)
2755             rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds);
2756
2757         if (rc != KSUCCESS)
2758         {
2759             if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, CellName, RealmName, 0)) == KSUCCESS)
2760             {
2761                 if ((rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds)) != KSUCCESS)
2762                 {
2763                     goto cleanup;
2764                 }
2765             }
2766             else if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, "", RealmName, 0)) == KSUCCESS)
2767             {
2768                 if ((rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds)) != KSUCCESS)
2769                 {
2770                     goto cleanup;
2771                 }
2772             }
2773             else
2774             {
2775                 goto cleanup;
2776             }
2777         }
2778 #else
2779         goto cleanup;
2780 #endif
2781     }
2782
2783     memset(&aserver, '\0', sizeof(aserver));
2784     strncpy(aserver.name, ServiceName, MAXKTCNAMELEN - 1);
2785     strncpy(aserver.cell, CellName, MAXKTCREALMLEN - 1);
2786
2787     strcpy(username, creds.pname);
2788     if (creds.pinst[0]) 
2789     {
2790         strcat(username, ".");
2791         strcat(username, creds.pinst);
2792     }
2793
2794     memset(&atoken, '\0', sizeof(atoken));
2795     atoken.kvno = creds.kvno;
2796     atoken.startTime = creds.issue_date;
2797     atoken.endTime = creds.issue_date + KFW_KRB4_life_to_time(0,creds.lifetime);
2798     memcpy(&atoken.sessionKey, creds.session, 8);
2799     atoken.ticketLen = creds.ticket_st.length;
2800     memcpy(atoken.ticket, creds.ticket_st.dat, atoken.ticketLen);
2801
2802   retry_gettoken:
2803     rc = pktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient);
2804     if (rc != 0 && rc != KTC_NOENT && rc != KTC_NOCELL) {
2805         if ( rc == KTC_NOCM && retry < 20 ) {
2806             Sleep(500);
2807             retry++;
2808             goto retry_gettoken;
2809         }
2810         KFW_AFS_error(rc, "ktc_GetToken()");
2811         code = rc;
2812         goto cleanup;
2813     }
2814
2815     if (atoken.kvno == btoken.kvno &&
2816         atoken.ticketLen == btoken.ticketLen &&
2817         !memcmp(&atoken.sessionKey, &btoken.sessionKey, sizeof(atoken.sessionKey)) &&
2818         !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) 
2819     {
2820         goto cleanup;
2821     }
2822
2823     // * Reset the "aclient" structure before we call ktc_SetToken.
2824     // * This structure was first set by the ktc_GetToken call when
2825     // * we were comparing whether identical tokens already existed.
2826
2827     strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
2828     strcpy(aclient.instance, "");
2829     strncpy(aclient.cell, creds.realm, MAXKTCREALMLEN - 1);
2830
2831     if (rc = pktc_SetToken(&aserver, &atoken, &aclient, 0))
2832     {
2833         KFW_AFS_error(rc, "ktc_SetToken()");
2834         code = rc;
2835         goto cleanup;
2836     }
2837
2838   cleanup:
2839     if (client_principal)
2840         pkrb5_free_principal(ctx,client_principal);
2841     /* increds.client == client_principal */
2842     if (increds.server)
2843         pkrb5_free_principal(ctx,increds.server);
2844     if (cc && (cc != alt_cc))
2845         pkrb5_cc_close(ctx, cc);
2846     if (ctx && (ctx != alt_ctx))
2847         pkrb5_free_context(ctx);
2848
2849    return(code);
2850 }
2851
2852 /**************************************/
2853 /* afs_realm_of_cell():               */
2854 /**************************************/
2855 static char *
2856 afs_realm_of_cell(afsconf_cell *cellconfig)
2857 {
2858     static char krbrlm[REALM_SZ+1]="";
2859     krb5_context  ctx = 0;
2860     char ** realmlist=NULL;
2861     krb5_error_code r;
2862
2863     if (!cellconfig)
2864         return 0;
2865
2866     if (!pkrb5_init_context)
2867         return 0;
2868
2869     r = pkrb5_init_context(&ctx); 
2870     if ( !r )
2871         r = pkrb5_get_host_realm(ctx, cellconfig->hostName[0], &realmlist);
2872     if ( !r && realmlist && realmlist[0] ) {
2873         strcpy(krbrlm, realmlist[0]);
2874         pkrb5_free_host_realm(ctx, realmlist);
2875     }
2876     if (ctx)
2877         pkrb5_free_context(ctx);
2878
2879     if ( !krbrlm[0] )
2880     {
2881         char *s = krbrlm;
2882         char *t = cellconfig->name;
2883         int c;
2884
2885         while (c = *t++)
2886         {
2887             if (islower(c)) c=toupper(c);
2888             *s++ = c;
2889         }
2890         *s++ = 0;
2891     }
2892     return(krbrlm);
2893 }
2894
2895 /**************************************/
2896 /* get_cellconfig():                  */
2897 /**************************************/
2898 static int 
2899 get_cellconfig(char *cell, afsconf_cell *cellconfig, char *local_cell)
2900 {
2901     int rc;
2902     char newcell[MAXCELLCHARS+1];
2903
2904     local_cell[0] = (char)0;
2905     memset(cellconfig, 0, sizeof(*cellconfig));
2906
2907     /* WIN32: cm_GetRootCellName(local_cell) - NOTE: no way to get max chars */
2908     if (rc = pcm_GetRootCellName(local_cell))
2909     {
2910         return(rc);
2911     }
2912
2913     if (strlen(cell) == 0)
2914         strcpy(cell, local_cell);
2915
2916     /* WIN32: cm_SearchCellFile(cell, pcallback, pdata) */
2917     strcpy(cellconfig->name, cell);
2918
2919     return pcm_SearchCellFile(cell, newcell, get_cellconfig_callback, (void*)cellconfig);
2920 }
2921
2922 /**************************************/
2923 /* get_cellconfig_callback():         */
2924 /**************************************/
2925 static long 
2926 get_cellconfig_callback(void *cellconfig, struct sockaddr_in *addrp, char *namep)
2927 {
2928     afsconf_cell *cc = (afsconf_cell *)cellconfig;
2929
2930     cc->hostAddr[cc->numServers] = *addrp;
2931     strcpy(cc->hostName[cc->numServers], namep);
2932     cc->numServers++;
2933     return(0);
2934 }
2935
2936
2937 /**************************************/
2938 /* KFW_AFS_error():                  */
2939 /**************************************/
2940 void
2941 KFW_AFS_error(LONG rc, LPCSTR FailedFunctionName)
2942 {
2943     char message[256];
2944     const char *errText; 
2945
2946     // Using AFS defines as error messages for now, until Transarc 
2947     // gets back to me with "string" translations of each of these 
2948     // const. defines. 
2949     if (rc == KTC_ERROR)
2950       errText = "KTC_ERROR";
2951     else if (rc == KTC_TOOBIG)
2952       errText = "KTC_TOOBIG";
2953     else if (rc == KTC_INVAL)
2954       errText = "KTC_INVAL";
2955     else if (rc == KTC_NOENT)
2956       errText = "KTC_NOENT";
2957     else if (rc == KTC_PIOCTLFAIL)
2958       errText = "KTC_PIOCTLFAIL";
2959     else if (rc == KTC_NOPIOCTL)
2960       errText = "KTC_NOPIOCTL";
2961     else if (rc == KTC_NOCELL)
2962       errText = "KTC_NOCELL";
2963     else if (rc == KTC_NOCM)
2964       errText = "KTC_NOCM: The service, Transarc AFS Daemon, most likely is not started!";
2965     else
2966       errText = "Unknown error!";
2967
2968     sprintf(message, "%s\n(%s failed)", errText, FailedFunctionName);
2969
2970     if ( IsDebuggerPresent() ) {
2971         OutputDebugString(message);
2972         OutputDebugString("\n");
2973     }
2974     MessageBox(NULL, message, "AFS", MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND);
2975     return;
2976 }
2977
2978 static DWORD 
2979 GetServiceStatus(
2980     LPSTR lpszMachineName, 
2981     LPSTR lpszServiceName,
2982     DWORD *lpdwCurrentState) 
2983
2984     DWORD           hr               = NOERROR; 
2985     SC_HANDLE       schSCManager     = NULL; 
2986     SC_HANDLE       schService       = NULL; 
2987     DWORD           fdwDesiredAccess = 0; 
2988     SERVICE_STATUS  ssServiceStatus  = {0}; 
2989     BOOL            fRet             = FALSE; 
2990
2991     *lpdwCurrentState = 0; 
2992  
2993     fdwDesiredAccess = GENERIC_READ; 
2994  
2995     schSCManager = OpenSCManager(lpszMachineName,  
2996                                  NULL,
2997                                  fdwDesiredAccess); 
2998  
2999     if(schSCManager == NULL) 
3000     { 
3001         hr = GetLastError();
3002         goto cleanup; 
3003     } 
3004  
3005     schService = OpenService(schSCManager,
3006                              lpszServiceName,
3007                              fdwDesiredAccess); 
3008  
3009     if(schService == NULL) 
3010     { 
3011         hr = GetLastError();
3012         goto cleanup; 
3013     } 
3014  
3015     fRet = QueryServiceStatus(schService,
3016                               &ssServiceStatus); 
3017  
3018     if(fRet == FALSE) 
3019     { 
3020         hr = GetLastError(); 
3021         goto cleanup; 
3022     } 
3023  
3024     *lpdwCurrentState = ssServiceStatus.dwCurrentState; 
3025  
3026 cleanup: 
3027  
3028     CloseServiceHandle(schService); 
3029     CloseServiceHandle(schSCManager); 
3030  
3031     return(hr); 
3032
3033
3034 void
3035 UnloadFuncs(
3036     FUNC_INFO fi[], 
3037     HINSTANCE h
3038     )
3039 {
3040     int n;
3041     if (fi)
3042         for (n = 0; fi[n].func_ptr_var; n++)
3043             *(fi[n].func_ptr_var) = 0;
3044     if (h) FreeLibrary(h);
3045 }
3046
3047 int
3048 LoadFuncs(
3049     const char* dll_name, 
3050     FUNC_INFO fi[], 
3051     HINSTANCE* ph,  // [out, optional] - DLL handle
3052     int* pindex,    // [out, optional] - index of last func loaded (-1 if none)
3053     int cleanup,    // cleanup function pointers and unload on error
3054     int go_on,      // continue loading even if some functions cannot be loaded
3055     int silent      // do not pop-up a system dialog if DLL cannot be loaded
3056     )
3057 {
3058     HINSTANCE h;
3059     int i, n, last_i;
3060     int error = 0;
3061     UINT em;
3062
3063     if (ph) *ph = 0;
3064     if (pindex) *pindex = -1;
3065
3066     for (n = 0; fi[n].func_ptr_var; n++)
3067         *(fi[n].func_ptr_var) = 0;
3068
3069     if (silent)
3070         em = SetErrorMode(SEM_FAILCRITICALERRORS);
3071     h = LoadLibrary(dll_name);
3072     if (silent)
3073         SetErrorMode(em);
3074
3075     if (!h)
3076         return 0;
3077
3078     last_i = -1;
3079     for (i = 0; (go_on || !error) && (i < n); i++)
3080     {
3081         void* p = (void*)GetProcAddress(h, fi[i].func_name);
3082         if (!p)
3083             error = 1;
3084         else
3085         {
3086             last_i = i;
3087             *(fi[i].func_ptr_var) = p;
3088         }
3089     }
3090     if (pindex) *pindex = last_i;
3091     if (error && cleanup && !go_on) {
3092         for (i = 0; i < n; i++) {
3093             *(fi[i].func_ptr_var) = 0;
3094         }
3095         FreeLibrary(h);
3096         return 0;
3097     }
3098     if (ph) *ph = h;
3099     if (error) return 0;
3100     return 1;
3101 }
3102
3103 #ifdef USE_FSPROBE
3104 // Cell Accessibility Functions
3105 // based on work originally submitted to the CMU Computer Club
3106 // by Jeffrey Hutzelman
3107 //
3108 // These would work great if the fsProbe interface had been 
3109 // ported to Windows
3110
3111 static 
3112 void probeComplete()
3113 {
3114     fsprobe_Cleanup(1);
3115     rx_Finalize();
3116 }
3117
3118 struct ping_params {
3119     unsigned short port;            // in
3120     int            retry_delay;     // in seconds
3121     int            verbose;         // in
3122     struct {
3123         int        wait;            // in seconds
3124         int        retry;           // in attempts
3125     }   host;
3126     int            max_hosts;       // in
3127     int            hosts_attempted; // out
3128 }
3129
3130 // the fsHandler is where we receive the answer to the probe
3131 static 
3132 int fsHandler(void)
3133 {
3134     ping_count = fsprobe_Results.probeNum;
3135     if (!*fsprobe_Results.probeOK)
3136     {
3137         ok_count++;
3138         if (waiting) complete();
3139     }
3140     if (ping_count == retry) 
3141         complete();
3142     return 0;
3143 }
3144
3145 // ping_fs is a callback routine meant to be called from within
3146 // cm_SearchCellFile() or cm_SearchCellDNS()
3147 static long 
3148 pingFS(void *ping_params, struct sockaddr_in *addrp, char *namep)
3149 {
3150     int rc;
3151     struct ping_params * pp = (struct ping_params *) ping_params;
3152
3153     if ( pp->max_hosts && pp->hosts_attempted >= pp->max_hosts )
3154         return 0;
3155
3156     pp->hosts_attempted++;
3157
3158     if (pp->port && addrp->sin_port != htons(pp->port))
3159         addrp->sin_port = htons(pp->port);
3160
3161     rc = fsprobe_Init(1, addrp, pp->retry_delay, fsHandler, pp->verbose);
3162     if (rc)
3163     {
3164         fprintf(stderr, "fsprobe_Init failed (%d)\n", rc);
3165         fsprobe_Cleanup(1);
3166         return 0;
3167     }
3168
3169     for (;;)
3170     {
3171         tv.tv_sec = pp->host.wait;
3172         tv.tv_usec = 0;
3173         if (IOMGR_Select(0, 0, 0, 0, &tv)) 
3174             break;
3175     }
3176     probeComplete();
3177     return(0);
3178 }
3179
3180
3181 static BOOL
3182 pingCell(char *cell)
3183 {
3184     int rc;
3185     char rootcell[MAXCELLCHARS+1];
3186     char newcell[MAXCELLCHARS+1];
3187     struct ping_params pp;
3188
3189     memset(&pp, 0, sizeof(struct ping_params));
3190
3191     if (!cell || strlen(cell) == 0) {
3192         /* WIN32 NOTE: no way to get max chars */
3193         if (rc = pcm_GetRootCellName(rootcell))
3194             return(FALSE);
3195         cell = rootcell;
3196     }
3197
3198     pp.port = 7000; // AFS FileServer
3199     pp.retry_delay = 10;
3200     pp.max_hosts = 3;
3201     pp.host.wait = 30;
3202     pp.host.retry = 0;
3203     pp.verbose = 1;
3204
3205     /* WIN32: cm_SearchCellFile(cell, pcallback, pdata) */
3206     rc = pcm_SearchCellFile(cell, newcell, pingFS, (void *)&pp);
3207 }
3208 #endif /* USE_FSPROBE */
3209  
3210 // These two items are imported from afscreds.h 
3211 // but it cannot be included without causing conflicts
3212 #define c100ns1SECOND        (LONGLONG)10000000
3213 static void 
3214 TimeToSystemTime (SYSTEMTIME *pst, time_t TimeT)
3215 {
3216     struct tm *pTime;
3217     memset (pst, 0x00, sizeof(SYSTEMTIME));
3218
3219     if ((pTime = localtime (&TimeT)) != NULL)
3220     {
3221         pst->wYear = pTime->tm_year + 1900;
3222         pst->wMonth = pTime->tm_mon + 1;
3223         pst->wDayOfWeek = pTime->tm_wday;
3224         pst->wDay = pTime->tm_mday;
3225         pst->wHour = pTime->tm_hour;
3226         pst->wMinute = pTime->tm_min;
3227         pst->wSecond = pTime->tm_sec;
3228         pst->wMilliseconds = 0;
3229     }
3230 }
3231
3232 void
3233 ObtainTokensFromUserIfNeeded(HWND hWnd)
3234 {
3235     char * rootcell = NULL;
3236     char   cell[MAXCELLCHARS+1] = "";
3237     char   password[PROBE_PASSWORD_LEN+1];
3238     krb5_data pwdata;
3239     afsconf_cell cellconfig;
3240     struct ktc_principal    aserver;
3241     struct ktc_principal    aclient;
3242     struct ktc_token    atoken;
3243     krb5_context ctx = 0;
3244     krb5_timestamp now = 0;
3245     krb5_error_code code;
3246     int serverReachable = 0;
3247     int rc;
3248 #ifndef USE_FSPROBE
3249     krb5_ccache cc = 0;
3250     const char * realm = 0;
3251     krb5_principal principal = 0;
3252     char * pname = 0;
3253 #endif /* USE_FSPROBE */
3254     DWORD       CurrentState;
3255     char        HostName[64];
3256     int         use_kfw = KFW_is_available();
3257
3258     CurrentState = 0;
3259     memset(HostName, '\0', sizeof(HostName));
3260     gethostname(HostName, sizeof(HostName));
3261     if (GetServiceStatus(HostName, TRANSARCAFSDAEMON, &CurrentState) != NOERROR)
3262         return;
3263     if (CurrentState != SERVICE_RUNNING) {
3264         SendMessage(hWnd, WM_START_SERVICE, FALSE, 0L);
3265         return;
3266     }
3267
3268     if (!pkrb5_init_context)
3269         return;
3270
3271     if ( use_kfw ) {
3272         code = pkrb5_init_context(&ctx);
3273         if ( code ) goto cleanup;
3274     }
3275
3276     rootcell = (char *)GlobalAlloc(GPTR,MAXCELLCHARS+1);
3277     if ( !rootcell ) goto cleanup;
3278
3279     code = get_cellconfig(cell, (void*)&cellconfig, rootcell);
3280     if ( code ) goto cleanup;
3281
3282     memset(&aserver, '\0', sizeof(aserver));
3283     strcpy(aserver.name, "afs");
3284     strcpy(aserver.cell, rootcell);
3285
3286     rc = pktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
3287
3288     if ( use_kfw ) {
3289         code = pkrb5_timeofday(ctx, &now);
3290         if ( code ) 
3291             now = 0;
3292
3293         if (!rc && (now < atoken.endTime))
3294             goto cleanup;
3295
3296         if ( IsDebuggerPresent() ) {
3297             char message[256];
3298             sprintf(message,"KFW_AFS_klog() returns: %d  now = %ul  endTime = %ul\n",
3299                      rc, now, atoken.endTime);
3300             OutputDebugString(message);
3301         }
3302     } else {
3303         SYSTEMTIME stNow;
3304         FILETIME ftNow;
3305         FILETIME ftExpires;
3306         LONGLONG llNow;
3307         LONGLONG llExpires;
3308         SYSTEMTIME stExpires;
3309
3310         TimeToSystemTime (&stExpires, atoken.endTime);
3311         GetLocalTime (&stNow);
3312         SystemTimeToFileTime (&stNow, &ftNow);
3313         SystemTimeToFileTime (&stExpires, &ftExpires);
3314
3315         llNow = (((LONGLONG)ftNow.dwHighDateTime) << 32) + (LONGLONG)(ftNow.dwLowDateTime);
3316         llExpires = (((LONGLONG)ftExpires.dwHighDateTime) << 32) + (LONGLONG)(ftExpires.dwLowDateTime);
3317
3318         llNow /= c100ns1SECOND;
3319         llExpires /= c100ns1SECOND;
3320
3321         if (!rc && (llNow < llExpires))
3322             goto cleanup;
3323
3324         if ( IsDebuggerPresent() ) {
3325             char message[256];
3326             sprintf(message,"KFW_AFS_klog() returns: %d  now = %ul  endTime = %ul\n",
3327                      rc, llNow, llExpires);
3328             OutputDebugString(message);
3329         }
3330     }
3331
3332
3333 #ifdef USE_FSPROBE
3334     serverReachable = cellPing(NULL);
3335 #else
3336     if ( use_kfw ) {
3337         // If we can't use the FSProbe interface we can attempt to forge
3338         // a kinit and if we can back an invalid user error we know the
3339         // kdc is at least reachable
3340         realm = afs_realm_of_cell(&cellconfig);  // do not free
3341
3342         code = pkrb5_build_principal(ctx, &principal, strlen(realm),
3343                                      realm, PROBE_USERNAME, NULL, NULL);
3344         if ( code ) goto cleanup;
3345
3346         code = KFW_get_ccache(ctx, principal, &cc);
3347         if ( code ) goto cleanup;
3348
3349         code = pkrb5_unparse_name(ctx, principal, &pname);
3350         if ( code ) goto cleanup;
3351
3352         pwdata.data = password;
3353         pwdata.length = PROBE_PASSWORD_LEN;
3354         code = pkrb5_c_random_make_octets(ctx, &pwdata);
3355         if (code) {
3356             int i;
3357             for ( i=0 ; i<PROBE_PASSWORD_LEN ; i )
3358                 password[i] = 'x';
3359         }
3360         password[PROBE_PASSWORD_LEN] = '\0';
3361
3362         code = KFW_kinit(NULL, NULL, HWND_DESKTOP, 
3363                            pname, 
3364                            password,
3365                            5,
3366                            0,
3367                            0,
3368                            0,
3369                            1,
3370                            0);
3371         switch ( code ) {
3372         case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
3373         case KRB5KDC_ERR_CLIENT_REVOKED:
3374         case KRB5KDC_ERR_CLIENT_NOTYET:
3375         case KRB5KDC_ERR_PREAUTH_FAILED:
3376         case KRB5KDC_ERR_PREAUTH_REQUIRED:
3377         case KRB5KDC_ERR_PADATA_TYPE_NOSUPP:
3378             serverReachable = TRUE;
3379             break;
3380         default:
3381             serverReachable = FALSE;
3382         }
3383     } else {
3384         int i;
3385         for ( i=0 ; i<PROBE_PASSWORD_LEN ; i )
3386             password[i] = 'x';
3387
3388         code = ObtainNewCredentials(rootcell, PROBE_USERNAME, password);
3389         serverReachable = 1;
3390     }
3391 #endif
3392     if ( !serverReachable ) {
3393         if ( IsDebuggerPresent() )
3394             OutputDebugString("Server Unreachable\n");
3395         goto cleanup;
3396     }
3397
3398     if ( IsDebuggerPresent() )
3399         OutputDebugString("Server Reachable\n");
3400
3401     if ( use_kfw ) {
3402 #ifdef USE_MS2MIT
3403         KFW_import_windows_lsa();
3404 #endif /* USE_MS2MIT */
3405         KFW_AFS_renew_expiring_tokens();
3406         KFW_AFS_renew_token_for_cell(rootcell);
3407
3408         rc = pktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient);
3409         if (!rc && (now < atoken.endTime))
3410             goto cleanup;
3411     }
3412
3413     SendMessage(hWnd, WM_OBTAIN_TOKENS, FALSE, (long)rootcell);
3414     rootcell = NULL;    // rootcell freed by message receiver
3415
3416   cleanup:
3417     if (rootcell)
3418         GlobalFree(rootcell);
3419
3420 #ifndef USE_FSPROBE
3421         if (KFW_is_available()) {
3422     if ( pname )
3423         pkrb5_free_unparsed_name(ctx,pname);
3424     if ( principal )
3425         pkrb5_free_principal(ctx,principal);
3426     if (cc)
3427         pkrb5_cc_close(ctx,cc);
3428 #endif /* USE_FSPROBE */
3429     if (ctx)
3430         pkrb5_free_context(ctx);
3431         }
3432     return;
3433 }
3434
3435 // IP Change Monitoring Functions
3436 #include <Iphlpapi.h>
3437
3438 DWORD
3439 GetNumOfIpAddrs(void)
3440 {
3441     PMIB_IPADDRTABLE pIpAddrTable = NULL;
3442     ULONG            dwSize;
3443     DWORD            code;
3444     DWORD            index;
3445     DWORD            validAddrs = 0;
3446
3447     dwSize = 0;
3448     code = GetIpAddrTable(NULL, &dwSize, 0);
3449     if (code == ERROR_INSUFFICIENT_BUFFER) {
3450         pIpAddrTable = malloc(dwSize);
3451         code = GetIpAddrTable(pIpAddrTable, &dwSize, 0);
3452         for ( index=0; index < pIpAddrTable->dwNumEntries; index++ ) {
3453             if (pIpAddrTable->table[index].dwAddr != 0)
3454                 validAddrs++;
3455         }
3456         free(pIpAddrTable);
3457     }
3458     return validAddrs;
3459 }
3460
3461 void
3462 IpAddrChangeMonitor(void * hWnd)
3463 {
3464 #ifdef USE_OVERLAPPED
3465     HANDLE Handle = INVALID_HANDLE_VALUE;
3466     OVERLAPPED Ovlap;
3467 #endif /* USE_OVERLAPPED */
3468     DWORD Result;
3469     DWORD prevNumOfAddrs = GetNumOfIpAddrs();
3470     DWORD NumOfAddrs;
3471
3472     if ( !hWnd )
3473         return;
3474
3475     while ( TRUE ) {
3476 #ifdef USE_OVERLAPPED
3477         ZeroMemory(&Ovlap, sizeof(OVERLAPPED));
3478
3479         Result = NotifyAddrChange(&Handle,&Ovlap);
3480         if (Result != ERROR_IO_PENDING)
3481         {        
3482             printf("NotifyAddrChange() failed with error %d \n", Result);
3483             break;
3484         }
3485
3486         if ((Result = WaitForSingleObject(Handle,INFINITE)) == WAIT_FAILED)
3487         {
3488             printf("WaitForSingleObject() failed with error %d\n",
3489                     GetLastError());
3490             continue;
3491         }
3492
3493         if (GetOverlappedResult(Handle, &Ovlap,
3494                                  &DataTransfered, TRUE) == 0)
3495         {
3496             printf("GetOverlapped result failed %d \n",
3497                     GetLastError());
3498             break;
3499         }
3500
3501 #else
3502         Result = NotifyAddrChange(NULL,NULL);
3503 #endif
3504         
3505         NumOfAddrs = GetNumOfIpAddrs();
3506
3507         if ( IsDebuggerPresent() ) {
3508             char message[256];
3509             sprintf(message,"IPAddrChangeMonitor() NumOfAddrs: now %d was %d\n",
3510                     NumOfAddrs, prevNumOfAddrs);
3511             OutputDebugString(message);
3512         }
3513
3514         if ( NumOfAddrs != prevNumOfAddrs ) {
3515             // Give AFS Client Service a chance to notice and die
3516             // Or for network services to startup
3517             Sleep(2000);
3518             // this call should probably be mutex protected
3519             ObtainTokensFromUserIfNeeded(hWnd);
3520         }
3521         prevNumOfAddrs = NumOfAddrs;
3522     }
3523
3524 #ifdef USE_OVERLAPPED
3525     if (Handle != INVALID_HANDLE_VALUE)
3526         CloseHandle(Handle);
3527 #endif 
3528 }
3529
3530
3531 DWORD 
3532 IpAddrChangeMonitorInit(HWND hWnd)
3533 {
3534     DWORD status = ERROR_SUCCESS;
3535     HANDLE thread;
3536     ULONG  threadID = 0;
3537
3538     thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)IpAddrChangeMonitor,
3539                                     hWnd, 0, &threadID);
3540
3541     if (thread == NULL) {
3542         status = GetLastError();
3543     }
3544     CloseHandle(thread);
3545     return status;
3546 }
3547