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