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