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