9dde7ba851216251968cf034a4f60064c92496d1
[openafs.git] / src / WINNT / netidmgr_plugin / afsfuncs.c
1 /*
2  * Copyright (c) 2005,2006,2007, 2008 Secure Endpoints Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without
7  * restriction, including without limitation the rights to use, copy,
8  * modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24
25 /* $Id$ */
26
27 /* Disable the 'macro redefinition' warning which is getting
28    triggerred by a redefinition of the ENCRYPT and DECRYPT macros. */
29 #pragma warning (push)
30 #pragma warning (disable: 4005)
31
32 #include <afsconfig.h>
33 #include <afs/param.h>
34
35 #include<afscred.h>
36 #include<dynimport.h>
37 #include<krb5common.h>
38 #include<winsock2.h>
39 #include<afs/cm.h>
40
41 #pragma warning (pop)
42
43 static char *afs_realm_of_cell(afs_conf_cell *, BOOL);
44 static long afs_get_cellconfig_callback(void *, struct sockaddr_in *, char *, unsigned short ipRank);
45 static int afs_get_cellconfig(char *, afs_conf_cell *, char *);
46
47 BOOL
48 afs_is_running(void) {
49     DWORD CurrentState;
50
51     if (!AfsAvailable)
52         return FALSE;
53
54     if (GetServiceStatus(NULL, TRANSARCAFSDAEMON, 
55                          &CurrentState, NULL) != NOERROR)
56         return FALSE;
57     if (CurrentState != SERVICE_RUNNING)
58         return FALSE;
59
60     return TRUE;
61 }
62
63 int
64 afs_unlog(void)
65 {
66     long        rc;
67
68     if (!afs_is_running())
69         return 0;
70
71     rc = ktc_ForgetAllTokens();
72
73     return rc;
74 }
75
76 int
77 afs_unlog_cred(khm_handle cred)
78 {
79     long rc;
80     struct ktc_principal princ;
81     khm_size cbbuf;
82     wchar_t name[KCDB_MAXCCH_NAME];
83
84     if (!afs_is_running())
85         return 0;
86
87     cbbuf = sizeof(princ);
88     if(KHM_FAILED(kcdb_cred_get_attr(cred, afs_attr_server_princ, 
89                                      NULL, &princ, &cbbuf)))
90         return 1;
91
92     afs_princ_to_string(&princ, name, sizeof(name));
93
94     _report_cs1(KHERR_INFO, L"Destroying token %1!s!",
95                 _cstr(name));
96     _resolve();
97
98     rc = ktc_ForgetToken(&princ);
99
100     return rc;
101 }
102
103 /* convert a ktc_principal to a wchar_t string form that looks like
104     name.instance@cell return 0 if it worked. non-zero otherwise
105 */
106 int 
107 afs_princ_to_string(struct ktc_principal * p, 
108                     wchar_t * buf, 
109                     size_t cbbuf)
110 {
111     wchar_t wbuf[256];
112     int rv = 0;
113     int l;
114
115     l = AnsiStrToUnicode(wbuf, sizeof(wbuf), p->name);
116     wbuf[l] = L'\0';
117
118     rv = FAILED(StringCbCopy(buf, cbbuf, wbuf));
119     if(p->instance[0]) {
120         StringCbCat(buf, cbbuf, L".");
121         if((l = AnsiStrToUnicode(wbuf, sizeof(wbuf), p->instance)) > 0) {
122             wbuf[l] = L'\0';
123             rv = rv || FAILED(StringCbCat(buf, cbbuf, wbuf));
124         }
125         else
126             rv = 1;
127     }
128     if(p->cell[0]) {
129         rv = rv || FAILED(StringCbCat(buf, cbbuf, L"@"));
130         if((l = AnsiStrToUnicode(wbuf, sizeof(wbuf), p->cell)) > 0) {
131             wbuf[l] = L'\0';
132             rv = rv || FAILED(StringCbCat(buf, cbbuf, wbuf));
133         }
134         else
135             rv = 1;
136     }
137
138     return rv;
139 }
140
141 int 
142 afs_list_tokens(void)
143 {
144     int r;
145
146     kcdb_credset_flush(afs_credset);
147     r = afs_list_tokens_internal();
148     kcdb_credset_collect(NULL, afs_credset, NULL, afs_credtype_id, NULL);
149
150     if (r == 0) {
151         afs_icon_set_state(AFSICON_REPORT_TOKENS, afs_credset);
152     } else if (r == -1) {
153         afs_icon_set_state(AFSICON_SERVICE_STOPPED, NULL);
154     } else {
155         afs_icon_set_state(AFSICON_SERVICE_ERROR, NULL);
156     }
157
158     return r;
159 }
160
161 /* is the credential provided an AFS token and is it from the
162    specified cell? */
163 static khm_int32 KHMAPI 
164 afs_filter_by_cell(khm_handle cred, khm_int32 flags, void * rock)
165 {
166     wchar_t wcell[MAXCELLCHARS];
167     wchar_t * tcell;
168     khm_size cbsize;
169     khm_int32 type;
170
171     tcell = (wchar_t *) rock;
172
173     if(KHM_FAILED(kcdb_cred_get_type(cred, &type)) ||
174         type != afs_credtype_id)
175         return FALSE;
176
177     cbsize = sizeof(wcell);
178     if(KHM_FAILED(kcdb_cred_get_attr(cred, afs_attr_cell, 
179                                      NULL, wcell, &cbsize)))
180         return FALSE;
181
182     if(wcscmp(wcell, tcell))
183         return FALSE;
184
185     return TRUE;
186 }
187
188 struct token_filter_data {
189     wchar_t * cell;
190 };
191
192 khm_int32 KHMAPI
193 afs_filter_for_token(khm_handle cred, khm_int32 flags, void * rock) {
194     struct token_filter_data * pdata;
195     wchar_t ccell[MAXCELLCHARS];
196     khm_size cb;
197     khm_int32 ctype;
198
199     pdata = (struct token_filter_data *) rock;
200
201     if (KHM_FAILED(kcdb_cred_get_type(cred, &ctype)) ||
202         ctype != afs_credtype_id)
203
204         return 0;
205
206     cb = sizeof(ccell);
207
208     if (KHM_FAILED(kcdb_cred_get_attr(cred, afs_attr_cell,
209                                       NULL,
210                                       ccell,
211                                       &cb)) ||
212         _wcsicmp(ccell, pdata->cell))
213
214         return 0;
215
216     return 1;
217 }
218
219 khm_handle
220 afs_find_token(khm_handle credset, wchar_t * cell) {
221     struct token_filter_data fdata;
222     khm_handle cred = NULL;
223
224     fdata.cell = cell;
225
226     if (KHM_FAILED(kcdb_credset_find_filtered(credset,
227                                               -1,
228                                               afs_filter_for_token,
229                                               &fdata,
230                                               &cred,
231                                               NULL)))
232         return NULL;
233     else
234         return cred;
235 }
236
237 static khm_int32 KHMAPI 
238 afs_filter_krb5_tkt(khm_handle cred, khm_int32 flags, void * rock) 
239 {
240     wchar_t cname[KCDB_CRED_MAXCCH_NAME];
241     khm_int32 type;
242     wchar_t * tcell;
243     wchar_t * t, *tkt_cell;
244     khm_size cbsize;
245
246     tcell = (wchar_t *) rock;
247
248     if(KHM_FAILED(kcdb_cred_get_type(cred, &type)) ||
249        type != krb5_credtype_id)
250         return FALSE;
251
252     cbsize = sizeof(cname);
253     if (KHM_FAILED(kcdb_cred_get_name(cred, cname, &cbsize)))
254         return FALSE;
255
256     if (!wcsncmp(cname, L"afs/", 4)) {
257
258         tkt_cell = cname + 4;
259
260         t = wcschr(tkt_cell, L'@');
261         if (t == NULL)
262             return FALSE;
263         *t = L'\0';
264
265     } else if (!wcsncmp(cname, L"afs@", 4)) {
266
267         tkt_cell = cname + 4;
268
269     } else {
270         return FALSE;
271     }
272
273     if (_wcsicmp(tcell, tkt_cell))
274         return FALSE;
275
276     return TRUE;
277 }
278
279 static khm_int32 KHMAPI 
280 afs_filter_krb4_tkt(khm_handle cred, khm_int32 flags, void * rock) 
281 {
282     wchar_t cname[KCDB_CRED_MAXCCH_NAME];
283     khm_int32 type;
284     wchar_t * tcell;
285     wchar_t * t, *tkt_cell;
286     khm_size cbsize;
287
288     tcell = (wchar_t *) rock;
289
290     if(KHM_FAILED(kcdb_cred_get_type(cred, &type)) ||
291        type != krb4_credtype_id)
292         return FALSE;
293
294     cbsize = sizeof(cname);
295     if (KHM_FAILED(kcdb_cred_get_name(cred, cname, &cbsize)))
296         return FALSE;
297
298     if (!wcsncmp(cname, L"afs.", 4)) {
299
300         tkt_cell = cname + 4;
301
302         t = wcschr(tkt_cell, L'@');
303         if (t == NULL)
304             return FALSE;
305         *t = L'\0';
306
307     } else if (!wcsncmp(cname, L"afs@", 4)) {
308
309         tkt_cell = cname + 4;
310
311     } else {
312         return FALSE;
313     }
314
315     if (_wcsicmp(tcell, tkt_cell))
316         return FALSE;
317
318     return TRUE;
319 }
320
321 /* collects all AFS tokens to the root credential set using the
322    generic afs_credset credential set
323    */
324 int
325 afs_list_tokens_internal(void)
326 {
327     struct ktc_principal    aserver;
328     struct ktc_principal    aclient;
329     struct ktc_token        atoken;
330     int                     cellNum;
331     int                     BreakAtEnd;
332     wchar_t                 idname[256];
333     wchar_t                 crname[256];
334     wchar_t                 location[256];
335     wchar_t                 *cell;
336
337     DWORD                   rc;
338
339     khm_handle              ident = NULL;
340     khm_handle              cred = NULL;
341     afs_tk_method           method;
342
343     FILETIME                ft;
344
345     if (!afs_is_running())
346         return -1;
347
348     kcdb_credset_flush(afs_credset);
349
350     LoadString(hResModule, IDS_DEF_LOCATION, location, ARRAYLENGTH(location));
351
352     BreakAtEnd = 0;
353     cellNum = 0;
354     while (1) 
355     {
356         memset(&aserver, 0, sizeof(aserver));
357         if (rc = ktc_ListTokens(cellNum, &cellNum, &aserver))
358         {
359             if (rc != KTC_NOENT)
360                 return(-2);
361
362             if (BreakAtEnd == 1)
363                 break;
364         }
365         BreakAtEnd = 1;
366         memset(&atoken, '\0', sizeof(atoken));
367         if (rc = ktc_GetToken(&aserver, &atoken, sizeof(atoken), &aclient))
368         {
369             if (rc == KTC_ERROR)
370                 return(-3);
371
372             continue;
373         }
374
375 #if 0
376         /* failed attempt at trying to figure out the principal name from
377            the token.  The ticket that is attached to the token is not
378            in a form that is useful at this point */
379         idname[0] = L'\0';
380         if(atoken.kvno == RXKAD_TKT_TYPE_KERBEROS_V5) {
381             krb5_context ctx = 0;
382             krb5_ccache cc = 0;
383             krb5_creds * k5c;
384             krb5_error_code code;
385             char * princ;
386
387             code = khm_krb5_initialize(&ctx, &cc);
388             if(code)
389                 goto _no_krb5;
390
391             k5c = (krb5_creds *) atoken.ticket;
392
393             code = pkrb5_unparse_name(ctx, k5c->client, &princ);
394             if(code)
395                 goto _no_krb5;
396
397             MultiByteToWideChar(CP_ACP, 0, princ, strlen(princ), idname, sizeof(idname)/sizeof(idname[0]));
398
399             pkrb5_free_unparsed_name(ctx, princ);
400 _no_krb5:
401             ;
402         }
403 #endif
404
405         method = AFS_TOKEN_AUTO;
406
407         afs_princ_to_string(&aclient, idname, sizeof(idname));
408
409         /* We need to figure out a good client name which we can use
410            to create an identity which looks familiar to the user.  No
411            good way of doing this, so we use a heuristic.
412
413            Note that, we use another heuristic to find out which
414            identity to associate the token with.
415    
416            ASSUMPTION:
417
418            The assumption here is that the principal for the token is
419            computed as follows:
420            
421            if realm != cell : principal looks like user@realm@cell
422            if realm == cell : principal looks like user@realm
423         
424            HEURISTIC:
425         
426            We strip the part of the string that follows the second '@'
427            sign to obtain the 'user@realm' part, which we use as the
428            credential name.  If there is no second '@', we use the
429            whole principal name. */
430         {
431             wchar_t * ats;
432
433             ats = wcschr(idname, L'@');
434             if(ats && (ats = wcschr(ats + 1, L'@')))
435                 *ats = L'\0';
436         }
437
438         afs_princ_to_string(&aserver, crname, sizeof(crname));
439
440         /* Ok, now we need to figure out which identity to associate
441            this token with.  This is a little bit tricky, and there is
442            currently no good way of determining the original identity
443            used to obtain the token if it was done outside of
444            NetIDMgr.  So we use a heuristic here.
445
446            REQUIREMENT:
447
448            Elsewhere, (actually in afsnewcreds.c) just after obtaining
449            AFS tokens through NetIDMgr, we enumerate the AFS tokens
450            and assign the root identity (used to obtain new creds)
451            with the AFS tokens.  This would still be there in the root
452            credential set when we list tokens later on.
453
454            HEURISTIC:
455
456            If there exists an AFS token in the root credential set for
457            the same cell, we associate this token with the same
458            identity as that credential.
459         */
460         cell = wcschr(crname, L'@');
461         if(cell) {
462             cell++;
463             if(!*cell)
464                 cell = NULL;
465         }
466
467         ident = NULL;
468         if(cell) {
469             khm_handle c;
470
471             if(KHM_SUCCEEDED(kcdb_credset_find_filtered(NULL, -1, 
472                                                         afs_filter_by_cell, 
473                                                         (void *) cell, 
474                                                         &c, NULL))) {
475                 khm_size cb;
476
477                 kcdb_cred_get_identity(c, &ident);
478                 cb = sizeof(method);
479                 kcdb_cred_get_attr(c, afs_attr_method, NULL,
480                                    &method, &cb);
481                 kcdb_cred_release(c);
482             }
483         }
484
485         /* If that failed, we have try another trick.  If there is a
486            Krb5 ticket of the form afs/<cell>@<realm> or afs@<CELL>
487            where <cell> matches our cell, then we pick the identity
488            off of that.
489
490            ASSUMPTION:
491
492            If Krb5 was used to obtain the token, then there is a Krb5
493            ticket of the form afs/<cell>@<REALM> or afs@<CELL> still
494            in the cache.  This is also true for Krb524 token
495            acquisition.
496
497            HEURISTIC:
498
499            If such a Krb5 ticket is found, use the identity of that
500            credential as the identity of the AFS token.
501
502         */
503         if (ident == NULL && cell != NULL) {
504             khm_handle c;
505
506             if(KHM_SUCCEEDED(kcdb_credset_find_filtered(NULL, -1, 
507                                                         afs_filter_krb5_tkt,
508                                                         (void *) cell, 
509                                                         &c, NULL))) {
510                 kcdb_cred_get_identity(c, &ident);
511                 /* this could be Krb5 or Krb524, so we leave method at
512                    AFS_TOKEN_AUTO. */
513                 method = AFS_TOKEN_AUTO;
514                 kcdb_cred_release(c);
515             }
516         }
517
518         /* If that didn't work either, we look for a Krb4 ticket of
519            the form afs.<cell>@<REALM> or afs@<CELL> which matches the
520            cell. 
521
522            ASSUMPTION:
523
524            If Krb4 was used to obtain an AFS token, then there should
525            be a Krb4 ticket of the form afs.<cell>@<REALM> or
526            afs@<CELL> in the cache.
527
528            HEURISTIC:
529
530            If such a ticket is found, then use the identity of that
531            credential as the identity of the AFS token.
532         */
533         if (ident == NULL && cell != NULL) {
534             khm_handle c;
535
536             if (krb4_credtype_id < 0) {
537                 kcdb_credtype_get_id(KRB4_CREDTYPE_NAME,
538                                      &krb4_credtype_id);
539             }
540
541             if (krb4_credtype_id >= 0 &&
542                 KHM_SUCCEEDED(kcdb_credset_find_filtered(NULL, -1,
543                                                          afs_filter_krb4_tkt,
544                                                          (void *) cell,
545                                                          &c, NULL))) {
546
547                 kcdb_cred_get_identity(c, &ident);
548                 kcdb_cred_release(c);
549                 method = AFS_TOKEN_KRB4;
550
551             }
552         }
553
554         /* Finally, we allow any extension plugins to give this a shot */
555         if (ident == NULL && cell != NULL) {
556             afs_ext_resolve_token(cell,
557                                   &atoken,
558                                   &aserver,
559                                   &aclient,
560                                   &ident,
561                                   &method);
562         }
563
564         /* One more thing to try.  If we have a cell->identity
565            mapping, then we try that. */
566         if (ident == NULL && cell != NULL) {
567             khm_handle h_cellmap;
568             wchar_t tidname[KCDB_IDENT_MAXCCH_NAME];
569             khm_size cb;
570
571             cb = sizeof(tidname);
572
573             if (KHM_SUCCEEDED(khc_open_space(csp_afscred,
574                                              L"Cells", 0, 
575                                              &h_cellmap))) {
576                 if (KHM_SUCCEEDED(khc_read_string(h_cellmap,
577                                                   cell,
578                                                   tidname,
579                                                   &cb))) {
580                     kcdb_identity_create(tidname,
581                                          KCDB_IDENT_FLAG_CREATE,
582                                          &ident);
583                 }
584                 khc_close_space(h_cellmap);
585             }
586         }
587
588         /* all else failed */
589         if(ident == NULL) {
590             if(KHM_FAILED(kcdb_identity_create(idname, 
591                                                KCDB_IDENT_FLAG_CREATE, 
592                                                &ident)))
593                 goto _exit;
594         }
595
596         if(KHM_FAILED(kcdb_cred_create(crname, ident, afs_credtype_id, &cred)))
597             goto _exit;
598
599         kcdb_cred_set_attr(cred, afs_attr_method, &method, sizeof(method));
600
601         TimetToFileTime(atoken.endTime, &ft);
602         kcdb_cred_set_attr(cred, KCDB_ATTR_EXPIRE, &ft, sizeof(ft));
603         if (atoken.startTime != 0) {
604             TimetToFileTime(atoken.startTime, &ft);
605             kcdb_cred_set_attr(cred, KCDB_ATTR_ISSUE, &ft, sizeof(ft));
606         }
607         kcdb_cred_set_attr(cred, afs_attr_client_princ, 
608                            &aclient, sizeof(aclient));
609         kcdb_cred_set_attr(cred, afs_attr_server_princ, 
610                            &aserver, sizeof(aserver));
611
612         if(cell) {
613             kcdb_cred_set_attr(cred, afs_attr_cell, cell, (khm_size)KCDB_CBSIZE_AUTO);
614         }
615
616         kcdb_cred_set_attr(cred, KCDB_ATTR_LOCATION, 
617                            location, (khm_size)KCDB_CBSIZE_AUTO);
618
619         kcdb_credset_add_cred(afs_credset, cred, -1);
620
621         /* both these calls are NULL pointer safe */
622         kcdb_cred_release(cred);
623         cred = NULL;
624         kcdb_identity_release(ident);
625         ident = NULL;
626     }
627
628 _exit:
629     if(ident)
630         kcdb_identity_release(ident);
631     if(cred)
632         kcdb_cred_release(cred);
633
634     return(0);
635 }
636
637
638 #define ALLOW_REGISTER 1
639 static int
640 ViceIDToUsername(char *username, 
641                  char *realm_of_user, 
642                  char *realm_of_cell,
643                  char * cell_to_use,
644                  struct ktc_principal *aclient, 
645                  struct ktc_principal *aserver, 
646                  struct ktc_token *atoken)
647 {
648     static char lastcell[MAXCELLCHARS+1] = { 0 };
649     static char confname[512] = { 0 };
650     char username_copy[BUFSIZ];
651     long viceId = ANONYMOUSID;          /* AFS uid of user */
652     int  status = 0;
653 #ifdef ALLOW_REGISTER
654     afs_int32 id;
655 #endif /* ALLOW_REGISTER */
656
657     if (confname[0] == '\0') {
658         StringCbCopyA(confname, sizeof(confname), AFSDIR_CLIENT_ETC_DIRPATH);
659     }
660
661     StringCbCopyA(lastcell, sizeof(lastcell), aserver->cell);
662
663     if (!pr_Initialize (0, confname, aserver->cell)) {
664         char sname[PR_MAXNAMELEN];
665         StringCbCopyA(sname, sizeof(sname), username);
666         status = pr_SNameToId (sname, &viceId);
667         pr_End();
668     }
669
670 #ifdef AFS_ID_TO_NAME
671     /*
672      * This is a crock, but it is Transarc's crock, so
673      * we have to play along in order to get the
674      * functionality.  The way the afs id is stored is
675      * as a string in the username field of the token.
676      * Contrary to what you may think by looking at
677      * the code for tokens, this hack (AFS ID %d) will
678      * not work if you change %d to something else.
679      */
680 #endif /* AFS_ID_TO_NAME */
681     /*
682      * This code is taken from cklog -- it lets people
683      * automatically register with the ptserver in foreign cells
684      */
685
686     /* copy the username because pr_CreateUser will lowercase it */
687     StringCbCopyA(username_copy, BUFSIZ, username);
688
689 #ifdef ALLOW_REGISTER
690     if (status == 0) {
691         if (viceId != ANONYMOUSID) {
692 #else /* ALLOW_REGISTER */
693             if ((status == 0) && (viceId != ANONYMOUSID))
694 #endif /* ALLOW_REGISTER */
695             {
696 #ifdef AFS_ID_TO_NAME
697                 StringCchPrintfA(username, BUFSIZ, "%s (AFS ID %d)", username_copy, (int) viceId);
698 #endif /* AFS_ID_TO_NAME */
699             }
700 #ifdef ALLOW_REGISTER
701         } else if (strcmp(realm_of_user, realm_of_cell) != 0) {
702             id = 0;
703             StringCbCopyA(aclient->name, sizeof(aclient->name), username);
704             StringCbCopyA(aclient->instance, sizeof(aclient->instance), "");
705             StringCbCopyA(aclient->cell, sizeof(aclient->cell), realm_of_user);
706             if (status = ktc_SetToken(aserver, atoken, aclient, 0))
707                 return status;
708             if (status = pr_Initialize(1L, confname, aserver->cell))
709                 return status;
710             status = pr_CreateUser(username, &id);
711             pr_End();
712             StringCbCopyA(username, BUFSIZ, username_copy);
713 #ifdef AFS_ID_TO_NAME
714             StringCchPrintfA(username, BUFSIZ, "%s (AFS ID %d)", username_copy, (int) viceId);
715 #endif /* AFS_ID_TO_NAME */
716         }
717     }
718 #endif /* ALLOW_REGISTER */
719     return status;
720 }
721
722
723 static void
724 copy_realm_of_ticket(krb5_context context, char * dest, size_t destlen, krb5_creds *v5cred) {
725     krb5_error_code code;
726     krb5_ticket *ticket;
727     size_t len;
728
729     code = pkrb5_decode_ticket(&v5cred->ticket, &ticket);
730     if (code == 0) {
731         len = krb5_princ_realm(context, ticket->server)->length;
732         if (len > destlen - 1)
733             len = destlen - 1;
734
735         StringCbCopyA(dest, len, krb5_princ_realm(context, ticket->server)->data);
736
737         pkrb5_free_ticket(context, ticket);
738     }
739 }
740
741 int
742 afs_klog(khm_handle identity,
743          char *service,
744          char *cell,
745          char *realm,
746          int LifeTime,
747          afs_tk_method method,
748          time_t * tok_expiration,
749          char *linkedCell) {
750
751     long        rc;
752     CREDENTIALS creds;
753     struct ktc_principal        aserver;
754     struct ktc_principal        aclient;
755     char        realm_of_user[MAXKTCREALMLEN]; /* Kerberos realm of user */
756     char        realm_of_cell[MAXKTCREALMLEN]; /* Kerberos realm of cell */
757     char        local_cell[MAXCELLCHARS+1];
758     char        Dmycell[MAXCELLCHARS+1];
759     struct ktc_token    atoken;
760     struct ktc_token    btoken;
761     afs_conf_cell       ak_cellconfig; /* General information about the cell */
762     char        RealmName[128];
763     char        CellName[128];
764     char        ServiceName[128];
765     khm_handle  confighandle = NULL;
766     khm_int32   supports_krb4 = (pkrb_get_tf_realm == NULL ? 0 : 1);
767     khm_int32   got524cred = 0;
768
769     /* signalling */
770     BOOL        bGotCreds = FALSE; /* got creds? */
771
772     if (tok_expiration)
773         *tok_expiration = (time_t) 0;
774
775     if (!afs_is_running()) {
776         _report_sr0(KHERR_WARNING, IDS_ERR_NOSERVICE);
777         return(0);
778     }
779
780     if ( !realm )   realm = "";
781     if ( !cell )    cell = "";
782     if ( !service ) service = "";
783
784     memset(&ak_cellconfig, 0, sizeof(ak_cellconfig));
785     memset(RealmName, '\0', sizeof(RealmName));
786     memset(CellName, '\0', sizeof(CellName));
787     memset(ServiceName, '\0', sizeof(ServiceName));
788     memset(realm_of_user, '\0', sizeof(realm_of_user));
789     memset(realm_of_cell, '\0', sizeof(realm_of_cell));
790     memset(Dmycell, '\0', sizeof(Dmycell));
791
792     // NULL or empty cell returns information on local cell
793     if (cell && cell[0])
794         StringCbCopyA(Dmycell, sizeof(Dmycell), cell);
795
796     rc = afs_get_cellconfig(Dmycell, &ak_cellconfig, local_cell);
797     if (rc) {
798         _reportf(L"afs_get_cellconfig returns %ld", rc);
799
800         _report_sr2(KHERR_ERROR, IDS_ERR_CELLCONFIG, _cstr(Dmycell), _int32(rc));
801         _suggest_sr(IDS_ERR_CELLCONFIG_S, KHERR_SUGGEST_NONE);
802         _resolve();
803         return(rc);
804     }
805
806     if (linkedCell && ak_cellconfig.linkedCell)
807         StringCbCopyA(linkedCell, MAXCELLCHARS, 
808                       ak_cellconfig.linkedCell);
809
810     StringCbCopyA(realm_of_cell, sizeof(realm_of_cell), 
811                   afs_realm_of_cell(&ak_cellconfig, FALSE));
812
813     if (strlen(service) == 0)
814         StringCbCopyA(ServiceName, sizeof(ServiceName), "afs");
815     else
816         StringCbCopyA(ServiceName, sizeof(ServiceName), service);
817
818     if (strlen(cell) == 0)
819         StringCbCopyA(CellName, sizeof(CellName), local_cell);
820     else
821         StringCbCopyA(CellName, sizeof(CellName), cell);
822
823     if (strlen(realm) == 0)
824         StringCbCopyA(RealmName, sizeof(RealmName), realm_of_cell);
825     else
826         StringCbCopyA(RealmName, sizeof(RealmName), realm);
827
828     memset(&creds, '\0', sizeof(creds));
829
830     /*** Kerberos 5 and 524 ***/
831
832     if (method == AFS_TOKEN_AUTO ||
833         method == AFS_TOKEN_KRB5 ||
834         method == AFS_TOKEN_KRB524) {
835
836         krb5_context   context = 0;
837         krb5_ccache    k5cc = 0;
838         krb5_creds     increds;
839         krb5_creds *   k5creds = 0;
840         krb5_error_code r;
841         krb5_principal client_principal = 0;
842         krb5_flags     flags = 0;
843
844         int         retry = 0;
845         int         len;
846         char        *p;
847
848         _reportf(L"Trying Kerberos 5");
849
850         if (!(r = khm_krb5_initialize(identity, &context, &k5cc))) {
851             int i;
852
853             memset(&increds, 0, sizeof(increds));
854
855             pkrb5_cc_get_principal(context, k5cc, &client_principal);
856             i = krb5_princ_realm(context, client_principal)->length;
857             if (i > MAXKTCREALMLEN-1) 
858                 i = MAXKTCREALMLEN-1;
859             StringCchCopyNA(realm_of_user, ARRAYLENGTH(realm_of_user),
860                             krb5_princ_realm(context, client_principal)->data,
861                             i);
862         } else {
863             _reportf(L"khm_krb5_initialize returns code %d", r);
864             goto try_krb4;
865         }
866
867         increds.client = client_principal;
868         increds.times.endtime = 0;
869         /* Ask for DES since that is what V4 understands */
870         increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
871
872 #ifdef KRB5_TC_NOTICKET
873         flags = KRB5_TC_OPENCLOSE;
874         r = pkrb5_cc_set_flags(context, k5cc, flags);
875 #endif
876         if (strlen(realm) != 0) {
877           retry_retcred_1:
878             /* First try Service/Cell@REALM */
879             if (r = pkrb5_build_principal(context, &increds.server,
880                                            (int) strlen(realm),
881                                            realm,
882                                            ServiceName,
883                                            CellName,
884                                            0)) {
885                 _reportf(L"krb5_build_principal returns %d", r);
886                 goto end_krb5;
887             }
888
889             r = pkrb5_get_credentials(context, 0, k5cc, &increds, &k5creds);
890             if (r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
891                 r == KRB5_ERR_HOST_REALM_UNKNOWN ||
892                 r == KRB5KRB_ERR_GENERIC /* Heimdal */) {
893                 /* Next try Service@REALM */
894                 pkrb5_free_principal(context, increds.server);
895                 r = pkrb5_build_principal(context, &increds.server,
896                                            (int) strlen(realm),
897                                            realm,
898                                            ServiceName,
899                                            0);
900                 if (r == 0)
901                     r = pkrb5_get_credentials(context, 0, k5cc, 
902                                                &increds, &k5creds);
903             }
904
905             /* Check to make sure we received a valid ticket; if not remove it
906              * and try again.  Perhaps there are two service tickets for the
907              * same service in the ccache.
908              */
909             if (r == 0 && k5creds && k5creds->times.endtime < time(NULL)) {
910                 pkrb5_free_principal(context, increds.server);
911                 pkrb5_cc_remove_cred(context, k5cc, 0, k5creds);
912                 pkrb5_free_creds(context, k5creds);
913                 k5creds = NULL;
914                 goto retry_retcred_1;
915             }
916         } else {
917           retry_retcred_2:
918             /* First try Service/Cell@_CLIENT_REALM */
919             if (r = pkrb5_build_principal(context, &increds.server,
920                                            (int) strlen(realm_of_user),
921                                            realm_of_user,
922                                            ServiceName,
923                                            CellName,
924                                            0)) {
925                 _reportf(L"krb5_build_principal returns %d", r);
926                 goto end_krb5;
927             }
928
929             r = pkrb5_get_credentials(context, 0, k5cc, &increds, &k5creds);
930             if (r == 0) {
931                 /* the user realm is a valid cell realm */
932                 StringCbCopyA(realm_of_cell, sizeof(realm_of_cell), realm_of_user);
933             }
934             if (r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
935                 r == KRB5_ERR_HOST_REALM_UNKNOWN ||
936                 r == KRB5KRB_ERR_GENERIC /* Heimdal */) {
937                 pkrb5_free_principal(context, increds.server);
938                 r = pkrb5_build_principal(context, &increds.server,
939                                            (int) strlen(realm_of_cell),
940                                            realm_of_cell,
941                                            ServiceName,
942                                            CellName,
943                                            0);
944                 if (r == 0)
945                     r = pkrb5_get_credentials(context, 0, k5cc, 
946                                                &increds, &k5creds);
947             }
948             if ((r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
949                  r == KRB5_ERR_HOST_REALM_UNKNOWN ||
950                  r == KRB5KRB_ERR_GENERIC /* Heimdal */) &&
951                  strlen(realm_of_cell) == 0) {
952                 StringCbCopyA(realm_of_cell, sizeof(realm_of_cell), 
953                                afs_realm_of_cell(&ak_cellconfig, TRUE));
954
955                 pkrb5_free_principal(context, increds.server);
956                 r = pkrb5_build_principal(context, &increds.server,
957                                            (int) strlen(realm_of_cell),
958                                            realm_of_cell,
959                                            ServiceName,
960                                            CellName,
961                                            0);
962                 if (r == 0)
963                     r = pkrb5_get_credentials(context, 0, k5cc, 
964                                                &increds, &k5creds);
965             }
966             if (r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
967                 r == KRB5_ERR_HOST_REALM_UNKNOWN ||
968                 r == KRB5KRB_ERR_GENERIC /* Heimdal */) {
969                 /* Next try Service@REALM */
970                 StringCbCopyA(realm_of_cell, sizeof(realm_of_cell), 
971                                afs_realm_of_cell(&ak_cellconfig, FALSE));
972
973                 pkrb5_free_principal(context, increds.server);
974                 r = pkrb5_build_principal(context, &increds.server,
975                                            (int) strlen(realm_of_cell),
976                                            realm_of_cell,
977                                            ServiceName,
978                                            0);
979                 if (r == 0)
980                     r = pkrb5_get_credentials(context, 0, k5cc, 
981                                                &increds, &k5creds);
982             }
983             if ((r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
984                  r == KRB5_ERR_HOST_REALM_UNKNOWN ||
985                  r == KRB5KRB_ERR_GENERIC /* Heimdal */) &&
986                  strlen(realm_of_cell) == 0) {
987                 /* Next try Service@REALM */
988                 StringCbCopyA(realm_of_cell, sizeof(realm_of_cell), 
989                                afs_realm_of_cell(&ak_cellconfig, TRUE));
990
991                 pkrb5_free_principal(context, increds.server);
992                 r = pkrb5_build_principal(context, &increds.server,
993                                            (int) strlen(realm_of_cell),
994                                            realm_of_cell,
995                                            ServiceName,
996                                            0);
997                 if (r == 0)
998                     r = pkrb5_get_credentials(context, 0, k5cc, 
999                                                &increds, &k5creds);
1000             }
1001
1002             if (r == 0 && strlen(realm_of_cell) == 0) 
1003                 copy_realm_of_ticket(context, realm_of_cell, sizeof(realm_of_cell), k5creds);
1004
1005             /* Check to make sure we received a valid ticket; if not remove it
1006              * and try again.  Perhaps there are two service tickets for the
1007              * same service in the ccache.
1008              */
1009             if (r == 0 && k5creds && k5creds->times.endtime < time(NULL)) {
1010                 pkrb5_free_principal(context, increds.server);
1011                 pkrb5_cc_remove_cred(context, k5cc, 0, k5creds);
1012                 pkrb5_free_creds(context, k5creds);
1013                 k5creds = NULL;
1014                 goto retry_retcred_2;
1015             }
1016         }
1017
1018         pkrb5_free_principal(context, increds.server);
1019         pkrb5_free_principal(context, client_principal);
1020         client_principal = 0;
1021 #ifdef KRB5_TC_NOTICKET
1022         flags = KRB5_TC_OPENCLOSE | KRB5_TC_NOTICKET;
1023         pkrb5_cc_set_flags(context, k5cc, flags);
1024 #endif
1025
1026         (void) pkrb5_cc_close(context, k5cc);
1027         k5cc = 0;
1028
1029         if (r) {
1030             _reportf(L"Code %d while getting credentials", r);
1031             k5creds = NULL;
1032             goto end_krb5;
1033         }
1034
1035         if ( k5creds->ticket.length > MAXKTCTICKETLEN ||
1036              method == AFS_TOKEN_KRB524) {
1037             goto try_krb524d;
1038         }
1039
1040         /* This code inserts the entire K5 ticket into the token */
1041
1042         _reportf(L"Trying K5 SetToken");
1043
1044         memset(&aserver, '\0', sizeof(aserver));
1045         StringCchCopyA(aserver.name, MAXKTCNAMELEN, ServiceName);
1046         StringCchCopyA(aserver.cell, MAXKTCREALMLEN, CellName);
1047
1048         memset(&atoken, '\0', sizeof(atoken));
1049         atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
1050         atoken.startTime = k5creds->times.starttime;
1051         atoken.endTime = k5creds->times.endtime;
1052         memcpy(&atoken.sessionKey, 
1053                k5creds->keyblock.contents, 
1054                k5creds->keyblock.length);
1055         atoken.ticketLen = k5creds->ticket.length;
1056         memcpy(atoken.ticket, k5creds->ticket.data, atoken.ticketLen);
1057
1058         if (tok_expiration)
1059             *tok_expiration = k5creds->times.endtime;
1060
1061     retry_gettoken5:
1062         rc = ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient);
1063         if (rc != 0 && rc != KTC_NOENT && rc != KTC_NOCELL) {
1064             if ( rc == KTC_NOCM && retry < 20 ) {
1065                 Sleep(500);
1066                 retry++;
1067                 goto retry_gettoken5;
1068             }
1069             goto try_krb524d;
1070         }
1071
1072         if (atoken.kvno == btoken.kvno &&
1073             atoken.ticketLen == btoken.ticketLen &&
1074             !memcmp(&atoken.sessionKey, &btoken.sessionKey, 
1075                     sizeof(atoken.sessionKey)) &&
1076             !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) {
1077
1078             /* success */
1079             if (k5creds && context)
1080                 pkrb5_free_creds(context, k5creds);
1081
1082             if (context)
1083                 pkrb5_free_context(context);
1084
1085             _reportf(L"Same token already exists");
1086             
1087             rc = 0;
1088             goto cleanup;
1089         }
1090
1091         // * Reset the "aclient" structure before we call ktc_SetToken.
1092         // * This structure was first set by the ktc_GetToken call when
1093         // * we were comparing whether identical tokens already existed.
1094
1095         len = min(k5creds->client->data[0].length,MAXKTCNAMELEN - 1);
1096         StringCchCopyNA(aclient.name, MAXKTCNAMELEN,
1097                         k5creds->client->data[0].data, len);
1098
1099         if ( k5creds->client->length > 1 ) {
1100             StringCbCatA(aclient.name, sizeof(aclient.name), ".");
1101             p = aclient.name + strlen(aclient.name);
1102             len = (int) min(k5creds->client->data[1].length,
1103                             MAXKTCNAMELEN - strlen(aclient.name) - 1);
1104             StringCchCopyNA(p, MAXKTCNAMELEN - strlen(aclient.name),
1105                             k5creds->client->data[1].data, len);
1106         }
1107
1108         aclient.instance[0] = '\0';
1109
1110         StringCbCopyA(aclient.cell, sizeof(aclient.cell), realm_of_cell);
1111
1112         StringCbCatA(aclient.name, sizeof(aclient.name), "@");
1113         p = aclient.name + strlen(aclient.name);
1114         len = (int) min(k5creds->client->realm.length,
1115                          MAXKTCNAMELEN - strlen(aclient.name) - 1);
1116         StringCchCopyNA(p, MAXKTCNAMELEN - strlen(aclient.name),
1117                         k5creds->client->realm.data, len);
1118
1119         ViceIDToUsername(aclient.name, realm_of_user, realm_of_cell, CellName, 
1120                          &aclient, &aserver, &atoken);
1121
1122         rc = ktc_SetToken(&aserver, &atoken, &aclient, 0);
1123         if (!rc) {
1124             /* success */
1125
1126             if (k5creds && context)
1127                 pkrb5_free_creds(context, k5creds);
1128
1129             if (context)
1130                 pkrb5_free_context(context);
1131             
1132             goto cleanup;
1133         }
1134
1135         _reportf(L"SetToken returns code %d", rc);
1136
1137     try_krb524d:
1138
1139         _reportf(L"Trying Krb524");
1140
1141         if (pkrb524_convert_creds_kdc && 
1142             (method == AFS_TOKEN_AUTO || method == AFS_TOKEN_KRB524)) {
1143             /* This requires krb524d to be running with the KDC */
1144             r = pkrb524_convert_creds_kdc(context, k5creds, &creds);
1145             if (r) {
1146                 _reportf(L"Code %d while converting credentials", r);
1147                 goto end_krb5;
1148             }
1149             rc = KSUCCESS;
1150             got524cred = 1;
1151             bGotCreds = TRUE;
1152         }
1153
1154     end_krb5:
1155         if (client_principal)
1156             pkrb5_free_principal(context, client_principal);
1157
1158         if (k5creds && context)
1159             pkrb5_free_creds(context, k5creds);
1160
1161         if (context)
1162             pkrb5_free_context(context);
1163     }
1164
1165     /* Kerberos 4 */
1166  try_krb4:
1167
1168     if (supports_krb4) {
1169         kcdb_identity_get_config(identity, 0, &confighandle);
1170         khc_read_int32(confighandle, L"Krb4Cred\\Krb4NewCreds", &supports_krb4);
1171         khc_close_space(confighandle); 
1172     }
1173
1174     if (!supports_krb4)
1175         _reportf(L"Kerberos 4 not configured");
1176
1177     if (!bGotCreds && supports_krb4 && 
1178         strlen(RealmName) < REALM_SZ && 
1179         (method == AFS_TOKEN_AUTO ||
1180          method == AFS_TOKEN_KRB4)) {
1181
1182         KTEXT_ST        ticket;
1183
1184         _reportf(L"Trying Kerberos 4");
1185
1186         if (!realm_of_user[0] ) {
1187             if ((rc = (*pkrb_get_tf_realm)((*ptkt_string)(), realm_of_user)) 
1188                 != KSUCCESS) {
1189                 /* can't determine realm of user */
1190                 _reportf(L"krb_get_tf_realm returns %d", rc);
1191                 goto end_krb4;
1192             }
1193         }
1194
1195         _reportf(L"Trying to find %S.%S@%S", ServiceName, CellName, RealmName);
1196         rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds);
1197         if (rc == NO_TKT_FIL) {
1198             // if the problem is that we have no krb4 tickets
1199             // do not attempt to continue
1200             _reportf(L"krb_get_cred returns %d (no ticket file)", rc);
1201             goto end_krb4;
1202         }
1203
1204         if (rc != KSUCCESS) {
1205             _reportf(L"Trying to find %S@%S", ServiceName, RealmName);
1206             rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds);
1207         }
1208
1209         if (rc != KSUCCESS) {
1210             _reportf(L"Trying to obtain new ticket");
1211             if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, 
1212                                      CellName, RealmName, 0))
1213                 == KSUCCESS) {
1214                 if ((rc = (*pkrb_get_cred)(ServiceName, CellName, 
1215                                            RealmName, &creds)) != KSUCCESS) {
1216                     goto end_krb4;
1217                 } else {
1218                     _reportf(L"Got %S.%S@%S", ServiceName, CellName, RealmName);
1219                 }
1220             } else if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, 
1221                                             "", RealmName, 0))
1222                        == KSUCCESS) {
1223                 if ((rc = (*pkrb_get_cred)(ServiceName, "", 
1224                                            RealmName, &creds)) != KSUCCESS) {
1225                     goto end_krb4;
1226                 } else {
1227                     _reportf(L"Got %S@%S", ServiceName, RealmName);
1228                 }
1229             } else {
1230                 goto end_krb4;
1231             }
1232         }
1233
1234         bGotCreds = TRUE;
1235
1236     end_krb4:
1237         ;
1238     }
1239
1240     if (bGotCreds) {
1241
1242         memset(&aserver, '\0', sizeof(aserver));
1243         StringCchCopyA(aserver.name, MAXKTCNAMELEN, ServiceName);
1244         StringCchCopyA(aserver.cell, MAXKTCREALMLEN, CellName);
1245
1246         memset(&atoken, '\0', sizeof(atoken));
1247         atoken.kvno = (short)creds.kvno;
1248         atoken.startTime = creds.issue_date;
1249         atoken.endTime = (*pkrb_life_to_time)(creds.issue_date,creds.lifetime);
1250         memcpy(&atoken.sessionKey, creds.session, 8);
1251         atoken.ticketLen = creds.ticket_st.length;
1252         memcpy(atoken.ticket, creds.ticket_st.dat, atoken.ticketLen);
1253
1254         if (tok_expiration)
1255             *tok_expiration = atoken.endTime;
1256
1257         if (!(rc = ktc_GetToken(&aserver, &btoken, 
1258                                 sizeof(btoken), &aclient)) &&
1259             atoken.kvno == btoken.kvno &&
1260             atoken.ticketLen == btoken.ticketLen &&
1261             !memcmp(&atoken.sessionKey, &btoken.sessionKey, 
1262                     sizeof(atoken.sessionKey)) &&
1263             !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) {
1264
1265             /* success! */
1266             rc = 0;
1267             goto cleanup;
1268         }
1269
1270         // Reset the "aclient" structure before we call ktc_SetToken.
1271         // This structure was first set by the ktc_GetToken call when
1272         // we were comparing whether identical tokens already existed.
1273
1274         StringCchCopyA(aclient.name, MAXKTCNAMELEN, creds.pname);
1275         if (creds.pinst[0]) {
1276             StringCchCatA(aclient.name, MAXKTCNAMELEN, ".");
1277             StringCchCatA(aclient.name, MAXKTCNAMELEN, creds.pinst);
1278         }
1279
1280         StringCbCopyA(aclient.instance, sizeof(aclient.instance), "");
1281
1282         StringCchCatA(aclient.name, MAXKTCNAMELEN, "@");
1283                 StringCchCatA(aclient.name, MAXKTCNAMELEN, got524cred ? realm_of_user : creds.realm);
1284
1285         StringCbCopyA(aclient.cell, sizeof(aclient.cell), CellName);
1286
1287         ViceIDToUsername(aclient.name, realm_of_user, realm_of_cell, CellName, 
1288                          &aclient, &aserver, &atoken);
1289
1290         if (rc = ktc_SetToken(&aserver, &atoken, &aclient, 0)) {
1291             afs_report_error(rc, "ktc_SetToken()");
1292             goto cleanup;
1293         }
1294     } else if (method == AFS_TOKEN_AUTO ||
1295                method >= AFS_TOKEN_USER) {
1296         /* we couldn't get a token using Krb5, Krb524 or Krb4, either
1297            because we couldn't get the necessary credentials or
1298            because the method was set to not use those.  Now we
1299            dispatch to any extensions to see if they have better
1300            luck. */
1301
1302         rc = !afs_ext_klog(method,
1303                            identity,
1304                            ServiceName,
1305                            CellName,
1306                            RealmName,
1307                            &ak_cellconfig,
1308                            LifeTime);
1309     } else {
1310         /* if the return code was not set, we should set it now.
1311            Otherwise we let the code go through. */
1312         if (!rc) {
1313             /* No tokens were obtained.  We should report something */
1314             _report_sr1(KHERR_ERROR, IDS_ERR_GENERAL,
1315                         _cptr(CellName));
1316             _resolve();
1317
1318             rc = KHM_ERROR_GENERAL;
1319         }
1320     }
1321
1322   cleanup:
1323     if (ak_cellconfig.linkedCell)
1324         free(ak_cellconfig.linkedCell);
1325
1326     return rc;
1327 }
1328
1329 /**************************************/
1330 /* afs_realm_of_cell():               */
1331 /**************************************/
1332 static char *
1333 afs_realm_of_cell(afs_conf_cell *cellconfig, BOOL referral_fallback)
1334 {
1335     char krbhst[MAX_HSTNM]="";
1336     static char krbrlm[MAXKTCREALMLEN+1]="";
1337     krb5_context  ctx = 0;
1338     char ** realmlist=NULL;
1339     krb5_error_code r = 0;
1340
1341     if (!cellconfig)
1342         return 0;
1343
1344     if (referral_fallback) {
1345         char * p;
1346         p = strchr(cellconfig->hostName[0], '.');
1347         if (p++)
1348             StringCbCopyA(krbrlm, sizeof(krbrlm), p);
1349         else
1350             StringCbCopyA(krbrlm, sizeof(krbrlm), cellconfig->name);
1351 #if _MSC_VER >= 1400
1352         _strupr_s(krbrlm, sizeof(krbrlm));
1353 #else
1354         _strupr(krbrlm);
1355 #endif
1356     } else {
1357         if ( pkrb5_init_context ) {
1358             r = pkrb5_init_context(&ctx); 
1359             if ( !r )
1360                 r = pkrb5_get_host_realm(ctx, cellconfig->hostName[0], &realmlist);
1361             if ( !r && realmlist && realmlist[0] ) {
1362                 StringCbCopyA(krbrlm, sizeof(krbrlm), realmlist[0]);
1363                 pkrb5_free_host_realm(ctx, realmlist);
1364             }
1365             if (ctx)
1366                 pkrb5_free_context(ctx);
1367         }
1368
1369         if (r) {
1370             if (pkrb_get_krbhst && pkrb_realmofhost) {
1371                 StringCbCopyA(krbrlm, sizeof(krbrlm), 
1372                                (char *)(*pkrb_realmofhost)(cellconfig->hostName[0]));
1373                 if ((*pkrb_get_krbhst)(krbhst, krbrlm, 1) != KSUCCESS)
1374                     krbrlm[0] = '\0';
1375             }
1376
1377             if ( !krbrlm[0] ) {
1378                 char * p;
1379                 p = strchr(cellconfig->hostName[0], '.');
1380                 if (p++)
1381                     StringCbCopyA(krbrlm, sizeof(krbrlm), p);
1382                 else
1383                     StringCbCopyA(krbrlm, sizeof(krbrlm), cellconfig->name);
1384 #if _MSC_VER >= 1400
1385                 _strupr_s(krbrlm, sizeof(krbrlm));
1386 #else
1387                 _strupr(krbrlm);
1388 #endif
1389             }
1390         }
1391     }
1392     return(krbrlm);
1393 }
1394
1395 /**************************************/
1396 /* afs_get_cellconfig():                  */
1397 /**************************************/
1398 static int 
1399 afs_get_cellconfig(char *cell, afs_conf_cell *cellconfig, char *local_cell)
1400 {
1401     int rc;
1402     int ttl = 0;
1403     char linkedCell[MAXCELLCHARS]="";
1404
1405     local_cell[0] = (char)0;
1406     memset(cellconfig, 0, sizeof(*cellconfig));
1407
1408     cellconfig->cbsize = sizeof(*cellconfig);
1409
1410     /* WIN32: cm_GetRootCellName(local_cell) - NOTE: no way to get max chars */
1411     if (rc = cm_GetRootCellName(local_cell)) {
1412         return(rc);
1413     }
1414
1415     if (strlen(cell) == 0)
1416         StringCbCopyA(cell, (MAXCELLCHARS+1) * sizeof(char), local_cell);
1417
1418     StringCbCopyA(cellconfig->name, (MAXCELLCHARS+1) * sizeof(char), cell);
1419
1420     rc = cm_SearchCellRegistry(1, cell, NULL, linkedCell, 
1421                                afs_get_cellconfig_callback, (void*) cellconfig);
1422     if (rc && rc != CM_ERROR_FORCE_DNS_LOOKUP)
1423         rc = cm_SearchCellFileEx(cell, NULL, linkedCell, afs_get_cellconfig_callback, 
1424                                  (void*)cellconfig);
1425     if(rc)
1426         rc = cm_SearchCellByDNS(cell, NULL, &ttl, 
1427                                 afs_get_cellconfig_callback, 
1428                                 (void*) cellconfig);
1429
1430     if (linkedCell[0])
1431         cellconfig->linkedCell = _strdup(linkedCell);
1432
1433     return rc;
1434 }
1435
1436 /**************************************/
1437 /* afs_get_cellconfig_callback():          */
1438 /**************************************/
1439 static long 
1440 afs_get_cellconfig_callback(void *cellconfig, 
1441                             struct sockaddr_in *addrp, 
1442                             char *namep,
1443                             unsigned short ipRank)
1444 {
1445     afs_conf_cell *cc = (afs_conf_cell *)cellconfig;
1446
1447     cc->hostAddr[cc->numServers] = *addrp;
1448     StringCbCopyA(cc->hostName[cc->numServers], 
1449                   sizeof(cc->hostName[0]), namep);
1450     cc->numServers++;
1451     return(0);
1452 }
1453
1454
1455 /**************************************/
1456 /* afs_report_error():           */
1457 /**************************************/
1458 void
1459 afs_report_error(LONG rc, LPCSTR FailedFunctionName)
1460 {
1461     char message[256];
1462     const char *errText; 
1463
1464     // Using AFS defines as error messages for now, until Transarc 
1465     // gets back to me with "string" translations of each of these 
1466     // const. defines. 
1467     if (rc == KTC_ERROR)
1468         errText = "KTC_ERROR";
1469     else if (rc == KTC_TOOBIG)
1470         errText = "KTC_TOOBIG";
1471     else if (rc == KTC_INVAL)
1472         errText = "KTC_INVAL";
1473     else if (rc == KTC_NOENT)
1474         errText = "KTC_NOENT";
1475     else if (rc == KTC_PIOCTLFAIL)
1476         errText = "KTC_PIOCTLFAIL";
1477     else if (rc == KTC_NOPIOCTL)
1478         errText = "KTC_NOPIOCTL";
1479     else if (rc == KTC_NOCELL)
1480         errText = "KTC_NOCELL";
1481     else if (rc == KTC_NOCM)
1482         errText = "KTC_NOCM: The service, Transarc AFS Daemon, most likely is not started!";
1483     else
1484         errText = "Unknown error!";
1485
1486     StringCbPrintfA(message, sizeof(message), 
1487                     "%s\n(%s failed)", errText, FailedFunctionName);
1488     _report_cs1(KHERR_ERROR, L"%1!S!", _cptr(message));
1489     _resolve();
1490     return;
1491 }
1492
1493 DWORD 
1494 GetServiceStatus(LPSTR lpszMachineName, 
1495                  LPSTR lpszServiceName, 
1496                  DWORD *lpdwCurrentState,
1497                  DWORD *lpdwWaitHint) 
1498
1499     DWORD           hr               = NOERROR; 
1500     SC_HANDLE       schSCManager     = NULL; 
1501     SC_HANDLE       schService       = NULL; 
1502     DWORD           fdwDesiredAccess = 0; 
1503     SERVICE_STATUS  ssServiceStatus  = {0}; 
1504     BOOL            fRet             = FALSE; 
1505
1506     *lpdwCurrentState = 0; 
1507  
1508     fdwDesiredAccess = GENERIC_READ; 
1509  
1510     schSCManager = OpenSCManagerA(lpszMachineName,  
1511                                   NULL,
1512                                   fdwDesiredAccess); 
1513  
1514     if(schSCManager == NULL) { 
1515         hr = GetLastError();
1516         goto cleanup; 
1517     } 
1518  
1519     schService = OpenServiceA(schSCManager,
1520                               lpszServiceName,
1521                               fdwDesiredAccess);
1522  
1523     if(schService == NULL) { 
1524         hr = GetLastError();
1525         goto cleanup; 
1526     } 
1527  
1528     fRet = QueryServiceStatus(schService,
1529                               &ssServiceStatus); 
1530  
1531     if(fRet == FALSE) { 
1532         hr = GetLastError(); 
1533         goto cleanup; 
1534     } 
1535  
1536     *lpdwCurrentState = ssServiceStatus.dwCurrentState; 
1537     if (lpdwWaitHint)
1538         *lpdwWaitHint = ssServiceStatus.dwWaitHint;
1539 cleanup: 
1540  
1541     CloseServiceHandle(schService); 
1542     CloseServiceHandle(schSCManager); 
1543  
1544     return(hr); 
1545
1546
1547 DWORD ServiceControl(LPSTR lpszMachineName, 
1548                      LPSTR lpszServiceName,
1549                      DWORD dwNewState) {
1550
1551     DWORD           hr               = NOERROR; 
1552     SC_HANDLE       schSCManager     = NULL; 
1553     SC_HANDLE       schService       = NULL; 
1554     DWORD           fdwDesiredAccess = 0; 
1555     SERVICE_STATUS  ssServiceStatus  = {0}; 
1556     BOOL            fRet             = FALSE; 
1557     DWORD           dwCurrentState   = 0;
1558
1559     dwCurrentState = 0; 
1560  
1561     fdwDesiredAccess = GENERIC_READ;
1562  
1563     schSCManager = OpenSCManagerA(lpszMachineName, NULL, 
1564                                   fdwDesiredAccess); 
1565  
1566     if(schSCManager == NULL) {
1567         hr = GetLastError();
1568         goto cleanup; 
1569     }
1570
1571     fdwDesiredAccess = GENERIC_READ | GENERIC_EXECUTE;
1572
1573     schService = OpenServiceA(schSCManager, lpszServiceName,
1574                               fdwDesiredAccess);
1575  
1576     if(schService == NULL) {
1577         hr = GetLastError();
1578         goto cleanup; 
1579     } 
1580  
1581     fRet = QueryServiceStatus(schService, &ssServiceStatus);
1582  
1583     if(fRet == FALSE) {
1584         hr = GetLastError(); 
1585         goto cleanup; 
1586     } 
1587  
1588     dwCurrentState = ssServiceStatus.dwCurrentState; 
1589
1590     if (dwCurrentState == SERVICE_STOPPED &&
1591         dwNewState == SERVICE_RUNNING) {
1592
1593         fRet = StartService(schService, 0, NULL);
1594
1595         if (fRet == FALSE) {
1596             hr = GetLastError();
1597             goto cleanup;
1598         }
1599     }
1600
1601     if (dwCurrentState == SERVICE_RUNNING &&
1602         dwNewState == SERVICE_STOPPED) {
1603         fRet = ControlService(schService, SERVICE_CONTROL_STOP, 
1604                               &ssServiceStatus);
1605
1606         if (fRet == FALSE) {
1607             hr = GetLastError();
1608             goto cleanup;
1609         }
1610     }
1611  
1612 cleanup: 
1613  
1614     CloseServiceHandle(schService); 
1615     CloseServiceHandle(schSCManager); 
1616  
1617     return(hr); 
1618 }
1619
1620 khm_boolean
1621 afs_check_for_cell_realm_match(khm_handle identity, char * cell) {
1622     char local_cell[MAXCELLCHARS];
1623     wchar_t wrealm[MAXCELLCHARS];
1624     wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
1625     wchar_t * atsign;
1626     khm_size cb;
1627     char * realm;
1628     afs_conf_cell cellconfig;
1629     int rc;
1630
1631     ZeroMemory(local_cell, sizeof(local_cell));
1632     ZeroMemory(&cellconfig, sizeof(cellconfig));
1633
1634     rc = afs_get_cellconfig(cell, &cellconfig, local_cell);
1635     if (rc)
1636         return FALSE;
1637
1638     realm = afs_realm_of_cell(&cellconfig, FALSE);
1639     if (cellconfig.linkedCell)
1640         free(cellconfig.linkedCell);
1641     if (!realm[0])      /* referral; assume it matches */
1642         return TRUE;
1643
1644     AnsiStrToUnicode(wrealm, sizeof(wrealm), realm);
1645
1646     cb = sizeof(idname);
1647     idname[0] = L'\0';
1648     kcdb_identity_get_name(identity, idname, &cb);
1649
1650     atsign = wcschr(idname, L'@');
1651     if (atsign && atsign[1] && !_wcsicmp(atsign + 1, wrealm)) {
1652         return TRUE;
1653     } else {
1654         return FALSE;
1655     }
1656 }