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