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