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