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