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