db9b72550b0709479946c21be2c55ea4d93a332a
[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         if (method == AFS_TOKEN_KRB524)
880             increds.session.keytype = ENCTYPE_DES_CBC_CRC;
881
882 #ifdef KRB5_TC_NOTICKET
883         flags = KRB5_TC_OPENCLOSE;
884         r = krb5_cc_set_flags(context, k5cc, flags);
885 #endif
886         if (strlen(realm) != 0) {
887         retry_retcred_1:
888             /* First try Service/Cell@REALM */
889             if (r = krb5_build_principal(context, &increds.server,
890                                          (int) strlen(realm),
891                                          realm,
892                                          ServiceName,
893                                          CellName,
894                                          0)) {
895                 _reportf(L"krb5_build_principal returns %d", r);
896                 goto end_krb5;
897             }
898
899             r = krb5_get_credentials(context, 0, k5cc, &increds, &k5creds);
900             if (r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
901                 r == KRB5_ERR_HOST_REALM_UNKNOWN ||
902                 r == KRB5KRB_ERR_GENERIC /* Heimdal */) {
903                 /* Next try Service@REALM */
904                 krb5_free_principal(context, increds.server);
905                 r = krb5_build_principal(context, &increds.server,
906                                          (int) strlen(realm),
907                                          realm,
908                                          ServiceName,
909                                          0);
910                 if (r == 0)
911                     r = krb5_get_credentials(context, 0, k5cc,
912                                              &increds, &k5creds);
913             }
914
915             /* Check to make sure we received a valid ticket; if not remove it
916              * and try again.  Perhaps there are two service tickets for the
917              * same service in the ccache.
918              */
919             if (r == 0 && k5creds && k5creds->times.endtime < time(NULL)) {
920                 krb5_free_principal(context, increds.server);
921                 krb5_cc_remove_cred(context, k5cc, 0, k5creds);
922                 krb5_free_creds(context, k5creds);
923                 k5creds = NULL;
924                 goto retry_retcred_1;
925             }
926         } else {
927           retry_retcred_2:
928             /* First try Service/Cell@_CLIENT_REALM */
929             if (r = krb5_build_principal(context, &increds.server,
930                                          (int) strlen(realm_of_user),
931                                          realm_of_user,
932                                          ServiceName,
933                                          CellName,
934                                          0)) {
935                 _reportf(L"krb5_build_principal returns %d", r);
936                 goto end_krb5;
937             }
938
939             r = krb5_get_credentials(context, 0, k5cc, &increds, &k5creds);
940             if (r == 0) {
941                 /* the user realm is a valid cell realm */
942                 StringCbCopyA(realm_of_cell, sizeof(realm_of_cell), realm_of_user);
943             }
944             if (r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
945                 r == KRB5_ERR_HOST_REALM_UNKNOWN ||
946                 r == KRB5KRB_ERR_GENERIC /* Heimdal */) {
947                 krb5_free_principal(context, increds.server);
948                 r = krb5_build_principal(context, &increds.server,
949                                          (int) strlen(realm_of_cell),
950                                          realm_of_cell,
951                                          ServiceName,
952                                          CellName,
953                                          0);
954                 if (r == 0)
955                     r = krb5_get_credentials(context, 0, k5cc,
956                                              &increds, &k5creds);
957             }
958             if ((r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
959                  r == KRB5_ERR_HOST_REALM_UNKNOWN ||
960                  r == KRB5KRB_ERR_GENERIC /* Heimdal */) &&
961                  strlen(realm_of_cell) == 0) {
962                 StringCbCopyA(realm_of_cell, sizeof(realm_of_cell),
963                               afs_realm_of_cell(&ak_cellconfig, TRUE));
964
965                 krb5_free_principal(context, increds.server);
966                 r = krb5_build_principal(context, &increds.server,
967                                          (int) strlen(realm_of_cell),
968                                          realm_of_cell,
969                                          ServiceName,
970                                          CellName,
971                                          0);
972                 if (r == 0)
973                     r = krb5_get_credentials(context, 0, k5cc,
974                                              &increds, &k5creds);
975             }
976             if (r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
977                 r == KRB5_ERR_HOST_REALM_UNKNOWN ||
978                 r == KRB5KRB_ERR_GENERIC /* Heimdal */) {
979                 /* Next try Service@REALM */
980                 StringCbCopyA(realm_of_cell, sizeof(realm_of_cell),
981                               afs_realm_of_cell(&ak_cellconfig, FALSE));
982
983                 krb5_free_principal(context, increds.server);
984                 r = krb5_build_principal(context, &increds.server,
985                                          (int) strlen(realm_of_cell),
986                                          realm_of_cell,
987                                          ServiceName,
988                                          0);
989                 if (r == 0)
990                     r = krb5_get_credentials(context, 0, k5cc,
991                                              &increds, &k5creds);
992             }
993             if ((r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
994                  r == KRB5_ERR_HOST_REALM_UNKNOWN ||
995                  r == KRB5KRB_ERR_GENERIC /* Heimdal */) &&
996                 strlen(realm_of_cell) == 0) {
997                 /* Next try Service@REALM */
998                 StringCbCopyA(realm_of_cell, sizeof(realm_of_cell),
999                               afs_realm_of_cell(&ak_cellconfig, TRUE));
1000
1001                 krb5_free_principal(context, increds.server);
1002                 r = krb5_build_principal(context, &increds.server,
1003                                          (int) strlen(realm_of_cell),
1004                                          realm_of_cell,
1005                                          ServiceName,
1006                                          0);
1007                 if (r == 0)
1008                     r = krb5_get_credentials(context, 0, k5cc,
1009                                              &increds, &k5creds);
1010             }
1011
1012             if (r == 0 && strlen(realm_of_cell) == 0)
1013                 copy_realm_of_ticket(context, realm_of_cell, sizeof(realm_of_cell), k5creds);
1014
1015             /* Check to make sure we received a valid ticket; if not
1016              * remove it and try again.  Perhaps there are two service
1017              * tickets for the same service in the ccache.
1018              */
1019             if (r == 0 && k5creds && k5creds->times.endtime < time(NULL)) {
1020                 krb5_free_principal(context, increds.server);
1021                 krb5_cc_remove_cred(context, k5cc, 0, k5creds);
1022                 krb5_free_creds(context, k5creds);
1023                 k5creds = NULL;
1024                 goto retry_retcred_2;
1025             }
1026         }
1027
1028         krb5_free_principal(context, increds.server);
1029         krb5_free_principal(context, client_principal);
1030         client_principal = 0;
1031 #ifdef KRB5_TC_NOTICKET
1032         flags = KRB5_TC_OPENCLOSE | KRB5_TC_NOTICKET;
1033         krb5_cc_set_flags(context, k5cc, flags);
1034 #endif
1035
1036         (void) krb5_cc_close(context, k5cc);
1037         k5cc = 0;
1038
1039         if (r) {
1040             _reportf(L"Code %d while getting credentials", r);
1041             k5creds = NULL;
1042             goto end_krb5;
1043         }
1044
1045 #ifdef USE_KRB4
1046         if ( k5creds->ticket.length > MAXKTCTICKETLEN ||
1047              method == AFS_TOKEN_KRB524) {
1048             goto try_krb524d;
1049         }
1050 #endif
1051
1052         /* This code inserts the entire K5 ticket into the token */
1053
1054         _reportf(L"Trying K5 SetToken");
1055
1056         memset(&aserver, '\0', sizeof(aserver));
1057         StringCchCopyA(aserver.name, MAXKTCNAMELEN, ServiceName);
1058         StringCchCopyA(aserver.cell, MAXKTCREALMLEN, CellName);
1059
1060         memset(&atoken, '\0', sizeof(atoken));
1061         atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
1062         atoken.startTime = k5creds->times.starttime;
1063         atoken.endTime = k5creds->times.endtime;
1064         if (tkt_DeriveDesKey(k5creds->session.keytype,
1065                              k5creds->session.keyvalue.data,
1066                              k5creds->session.keyvalue.length,
1067                              &atoken.sessionKey))
1068             goto cleanup;
1069         atoken.ticketLen = k5creds->ticket.length;
1070         memcpy(atoken.ticket, k5creds->ticket.data, atoken.ticketLen);
1071
1072         if (tok_expiration)
1073             *tok_expiration = k5creds->times.endtime;
1074
1075     retry_gettoken5:
1076         rc = ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient);
1077         if (rc != 0 && rc != KTC_NOENT && rc != KTC_NOCELL) {
1078             if ( rc == KTC_NOCM && retry < 20 ) {
1079                 Sleep(500);
1080                 retry++;
1081                 goto retry_gettoken5;
1082             }
1083 #ifdef USE_KRB4
1084             goto try_krb524d;
1085 #else
1086             goto end_krb5;
1087 #endif
1088         }
1089
1090         if (atoken.kvno == btoken.kvno &&
1091             atoken.ticketLen == btoken.ticketLen &&
1092             !memcmp(&atoken.sessionKey, &btoken.sessionKey,
1093                     sizeof(atoken.sessionKey)) &&
1094             !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) {
1095
1096             /* success */
1097             if (k5creds && context)
1098                 krb5_free_creds(context, k5creds);
1099
1100             if (context)
1101                 krb5_free_context(context);
1102
1103             _reportf(L"Same token already exists");
1104
1105             rc = 0;
1106             goto cleanup;
1107         }
1108
1109         // * Reset the "aclient" structure before we call ktc_SetToken.
1110         // * This structure was first set by the ktc_GetToken call when
1111         // * we were comparing whether identical tokens already existed.
1112
1113         StringCchCopyA(aclient.name, MAXKTCNAMELEN,
1114                        krb5_principal_get_comp_string(context, k5creds->client, 0));
1115
1116         if ( krb5_principal_get_num_comp(context, k5creds->client) > 1 ) {
1117             StringCbCatA(aclient.name, sizeof(aclient.name), ".");
1118             StringCbCatA(aclient.name, sizeof(aclient.name),
1119                          krb5_principal_get_comp_string(context, k5creds->client, 1));
1120         }
1121
1122         aclient.instance[0] = '\0';
1123
1124         StringCbCopyA(aclient.cell, sizeof(aclient.cell), realm_of_cell);
1125
1126         StringCbCatA(aclient.name, sizeof(aclient.name), "@");
1127         StringCbCatA(aclient.name, sizeof(aclient.name),
1128                      krb5_principal_get_realm(context, k5creds->client));
1129
1130         ViceIDToUsername(aclient.name, realm_of_user, realm_of_cell, CellName,
1131                          &aclient, &aserver, &atoken);
1132
1133         rc = ktc_SetToken(&aserver, &atoken, &aclient, 0);
1134         if (!rc) {
1135             /* success */
1136
1137             if (k5creds && context)
1138                 krb5_free_creds(context, k5creds);
1139
1140             if (context)
1141                 krb5_free_context(context);
1142
1143             goto cleanup;
1144         }
1145
1146         _reportf(L"SetToken returns code %d", rc);
1147
1148 #ifdef USE_KRB4
1149     try_krb524d:
1150
1151         _reportf(L"Trying Krb524");
1152
1153         if (krb524_convert_creds_kdc &&
1154             (method == AFS_TOKEN_AUTO || method == AFS_TOKEN_KRB524)) {
1155             /* This requires krb524d to be running with the KDC */
1156             r = krb524_convert_creds_kdc(context, k5creds, &creds);
1157             if (r) {
1158                 _reportf(L"Code %d while converting credentials", r);
1159                 goto end_krb5;
1160             }
1161             rc = KSUCCESS;
1162             got524cred = 1;
1163             bGotCreds = TRUE;
1164         }
1165 #endif
1166
1167     end_krb5:
1168         if (client_principal)
1169             krb5_free_principal(context, client_principal);
1170
1171         if (k5creds && context)
1172             krb5_free_creds(context, k5creds);
1173
1174         if (context)
1175             krb5_free_context(context);
1176     }
1177
1178 #ifdef USE_KRB4
1179     /* Kerberos 4 */
1180  try_krb4:
1181
1182     if (supports_krb4) {
1183         kcdb_identity_get_config(identity, 0, &confighandle);
1184         khc_read_int32(confighandle, L"Krb4Cred\\Krb4NewCreds", &supports_krb4);
1185         khc_close_space(confighandle);
1186     }
1187
1188     if (!supports_krb4)
1189         _reportf(L"Kerberos 4 not configured");
1190
1191     if (!bGotCreds && supports_krb4 &&
1192         strlen(RealmName) < REALM_SZ &&
1193         (method == AFS_TOKEN_AUTO ||
1194          method == AFS_TOKEN_KRB4)) {
1195
1196         KTEXT_ST        ticket;
1197
1198         _reportf(L"Trying Kerberos 4");
1199
1200         if (!realm_of_user[0] ) {
1201             if ((rc = (*pkrb_get_tf_realm)((*ptkt_string)(), realm_of_user))
1202                 != KSUCCESS) {
1203                 /* can't determine realm of user */
1204                 _reportf(L"krb_get_tf_realm returns %d", rc);
1205                 goto end_krb4;
1206             }
1207         }
1208
1209         _reportf(L"Trying to find %S.%S@%S", ServiceName, CellName, RealmName);
1210         rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds);
1211         if (rc == NO_TKT_FIL) {
1212             // if the problem is that we have no krb4 tickets
1213             // do not attempt to continue
1214             _reportf(L"krb_get_cred returns %d (no ticket file)", rc);
1215             goto end_krb4;
1216         }
1217
1218         if (rc != KSUCCESS) {
1219             _reportf(L"Trying to find %S@%S", ServiceName, RealmName);
1220             rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds);
1221         }
1222
1223         if (rc != KSUCCESS) {
1224             _reportf(L"Trying to obtain new ticket");
1225             if ((rc = (*pkrb_mk_req)(&ticket, ServiceName,
1226                                      CellName, RealmName, 0))
1227                 == KSUCCESS) {
1228                 if ((rc = (*pkrb_get_cred)(ServiceName, CellName,
1229                                            RealmName, &creds)) != KSUCCESS) {
1230                     goto end_krb4;
1231                 } else {
1232                     _reportf(L"Got %S.%S@%S", ServiceName, CellName, RealmName);
1233                 }
1234             } else if ((rc = (*pkrb_mk_req)(&ticket, ServiceName,
1235                                             "", RealmName, 0))
1236                        == KSUCCESS) {
1237                 if ((rc = (*pkrb_get_cred)(ServiceName, "",
1238                                            RealmName, &creds)) != KSUCCESS) {
1239                     goto end_krb4;
1240                 } else {
1241                     _reportf(L"Got %S@%S", ServiceName, RealmName);
1242                 }
1243             } else {
1244                 goto end_krb4;
1245             }
1246         }
1247
1248         bGotCreds = TRUE;
1249
1250     end_krb4:
1251         ;
1252     }
1253
1254     if (bGotCreds) {
1255
1256         memset(&aserver, '\0', sizeof(aserver));
1257         StringCchCopyA(aserver.name, MAXKTCNAMELEN, ServiceName);
1258         StringCchCopyA(aserver.cell, MAXKTCREALMLEN, CellName);
1259
1260         memset(&atoken, '\0', sizeof(atoken));
1261         atoken.kvno = (short)creds.kvno;
1262         atoken.startTime = creds.issue_date;
1263         atoken.endTime = (*pkrb_life_to_time)(creds.issue_date,creds.lifetime);
1264         memcpy(&atoken.sessionKey, creds.session, 8);
1265         atoken.ticketLen = creds.ticket_st.length;
1266         memcpy(atoken.ticket, creds.ticket_st.dat, atoken.ticketLen);
1267
1268         if (tok_expiration)
1269             *tok_expiration = atoken.endTime;
1270
1271         if (!(rc = ktc_GetToken(&aserver, &btoken,
1272                                 sizeof(btoken), &aclient)) &&
1273             atoken.kvno == btoken.kvno &&
1274             atoken.ticketLen == btoken.ticketLen &&
1275             !memcmp(&atoken.sessionKey, &btoken.sessionKey,
1276                     sizeof(atoken.sessionKey)) &&
1277             !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) {
1278
1279             /* success! */
1280             rc = 0;
1281             goto cleanup;
1282         }
1283
1284         // Reset the "aclient" structure before we call ktc_SetToken.
1285         // This structure was first set by the ktc_GetToken call when
1286         // we were comparing whether identical tokens already existed.
1287
1288         StringCchCopyA(aclient.name, MAXKTCNAMELEN, creds.pname);
1289         if (creds.pinst[0]) {
1290             StringCchCatA(aclient.name, MAXKTCNAMELEN, ".");
1291             StringCchCatA(aclient.name, MAXKTCNAMELEN, creds.pinst);
1292         }
1293
1294         StringCbCopyA(aclient.instance, sizeof(aclient.instance), "");
1295
1296         StringCchCatA(aclient.name, MAXKTCNAMELEN, "@");
1297         StringCchCatA(aclient.name, MAXKTCNAMELEN, got524cred ? realm_of_user : creds.realm);
1298
1299         StringCbCopyA(aclient.cell, sizeof(aclient.cell), CellName);
1300
1301         ViceIDToUsername(aclient.name, realm_of_user, realm_of_cell, CellName,
1302                          &aclient, &aserver, &atoken);
1303
1304         if (rc = ktc_SetToken(&aserver, &atoken, &aclient, 0)) {
1305             afs_report_error(rc, "ktc_SetToken()");
1306             goto cleanup;
1307         }
1308     }
1309 #endif
1310
1311     if (!bGotCreds &&
1312         (method == AFS_TOKEN_AUTO ||
1313          method >= AFS_TOKEN_USER)) {
1314             /* we couldn't get a token using Krb5, Krb524 or Krb4,
1315                either because we couldn't get the necessary
1316                credentials or because the method was set to not use
1317                those.  Now we dispatch to any extensions to see if
1318                they have better luck. */
1319
1320         rc = !afs_ext_klog(method,
1321                            identity,
1322                            ServiceName,
1323                            CellName,
1324                            RealmName,
1325                            &ak_cellconfig,
1326                            LifeTime);
1327     } else if (!bGotCreds) {
1328         /* if the return code was not set, we should set it now.
1329            Otherwise we let the code go through. */
1330         if (!rc) {
1331             /* No tokens were obtained.  We should report something */
1332             _report_sr1(KHERR_ERROR, IDS_ERR_GENERAL,
1333                         _cptr(CellName));
1334             _resolve();
1335
1336             rc = KHM_ERROR_GENERAL;
1337         }
1338     }
1339
1340   cleanup:
1341     if (ak_cellconfig.linkedCell)
1342         free(ak_cellconfig.linkedCell);
1343
1344     return rc;
1345 }
1346
1347 /**************************************/
1348 /* afs_realm_of_cell():               */
1349 /**************************************/
1350 static char *
1351 afs_realm_of_cell(afs_conf_cell *cellconfig, BOOL referral_fallback)
1352 {
1353     char krbhst[MAX_HSTNM]="";
1354     static char krbrlm[MAXKTCREALMLEN+1]="";
1355     krb5_context  ctx = 0;
1356     char ** realmlist=NULL;
1357     krb5_error_code r = 0;
1358
1359     if (!cellconfig)
1360         return 0;
1361
1362     if (referral_fallback) {
1363         char * p;
1364         p = strchr(cellconfig->hostName[0], '.');
1365         if (p++)
1366             StringCbCopyA(krbrlm, sizeof(krbrlm), p);
1367         else
1368             StringCbCopyA(krbrlm, sizeof(krbrlm), cellconfig->name);
1369 #if _MSC_VER >= 1400
1370         _strupr_s(krbrlm, sizeof(krbrlm));
1371 #else
1372         _strupr(krbrlm);
1373 #endif
1374     } else {
1375         r = krb5_init_context(&ctx);
1376         if ( !r )
1377             r = krb5_get_host_realm(ctx, cellconfig->hostName[0], &realmlist);
1378         if ( !r && realmlist && realmlist[0] ) {
1379             StringCbCopyA(krbrlm, sizeof(krbrlm), realmlist[0]);
1380             krb5_free_host_realm(ctx, realmlist);
1381         }
1382         if (ctx)
1383             krb5_free_context(ctx);
1384
1385 #ifdef USE_KRB4
1386         if (r) {
1387             if (pkrb_get_krbhst && pkrb_realmofhost) {
1388                 StringCbCopyA(krbrlm, sizeof(krbrlm),
1389                                (char *)(*pkrb_realmofhost)(cellconfig->hostName[0]));
1390                 if ((*pkrb_get_krbhst)(krbhst, krbrlm, 1) != KSUCCESS)
1391                     krbrlm[0] = '\0';
1392             }
1393
1394             if ( !krbrlm[0] ) {
1395                 char * p;
1396                 p = strchr(cellconfig->hostName[0], '.');
1397                 if (p++)
1398                     StringCbCopyA(krbrlm, sizeof(krbrlm), p);
1399                 else
1400                     StringCbCopyA(krbrlm, sizeof(krbrlm), cellconfig->name);
1401 #if _MSC_VER >= 1400
1402                 _strupr_s(krbrlm, sizeof(krbrlm));
1403 #else
1404                 _strupr(krbrlm);
1405 #endif
1406             }
1407         }
1408 #endif
1409     }
1410     return(krbrlm);
1411 }
1412
1413 /**************************************/
1414 /* afs_get_cellconfig():                  */
1415 /**************************************/
1416 static int
1417 afs_get_cellconfig(char *cell, afs_conf_cell *cellconfig, char *local_cell)
1418 {
1419     int rc;
1420     int ttl = 0;
1421     char linkedCell[MAXCELLCHARS]="";
1422
1423     local_cell[0] = (char)0;
1424     memset(cellconfig, 0, sizeof(*cellconfig));
1425
1426     cellconfig->cbsize = sizeof(*cellconfig);
1427
1428     /* WIN32: cm_GetRootCellName(local_cell) - NOTE: no way to get max chars */
1429     if (rc = cm_GetRootCellName(local_cell)) {
1430         return(rc);
1431     }
1432
1433     if (strlen(cell) == 0)
1434         StringCbCopyA(cell, (MAXCELLCHARS+1) * sizeof(char), local_cell);
1435
1436     StringCbCopyA(cellconfig->name, (MAXCELLCHARS+1) * sizeof(char), cell);
1437
1438     rc = cm_SearchCellRegistry(1, cell, NULL, linkedCell,
1439                                afs_get_cellconfig_callback, (void*) cellconfig);
1440     if (rc && rc != CM_ERROR_FORCE_DNS_LOOKUP)
1441         rc = cm_SearchCellFileEx(cell, NULL, linkedCell, afs_get_cellconfig_callback,
1442                                  (void*)cellconfig);
1443     if(rc)
1444         rc = cm_SearchCellByDNS(cell, NULL, &ttl,
1445                                 afs_get_cellconfig_callback,
1446                                 (void*) cellconfig);
1447
1448     if (linkedCell[0])
1449         cellconfig->linkedCell = strdup(linkedCell);
1450
1451     return rc;
1452 }
1453
1454 /**************************************/
1455 /* afs_get_cellconfig_callback():          */
1456 /**************************************/
1457 static long
1458 afs_get_cellconfig_callback(void *cellconfig,
1459                             struct sockaddr_in *addrp,
1460                             char *namep,
1461                             unsigned short ipRank)
1462 {
1463     afs_conf_cell *cc = (afs_conf_cell *)cellconfig;
1464
1465     cc->hostAddr[cc->numServers] = *addrp;
1466     StringCbCopyA(cc->hostName[cc->numServers],
1467                   sizeof(cc->hostName[0]), namep);
1468     cc->numServers++;
1469     return(0);
1470 }
1471
1472
1473 /**************************************/
1474 /* afs_report_error():           */
1475 /**************************************/
1476 void
1477 afs_report_error(LONG rc, LPCSTR FailedFunctionName)
1478 {
1479     char message[256];
1480     const char *errText;
1481
1482     // Using AFS defines as error messages for now, until Transarc
1483     // gets back to me with "string" translations of each of these
1484     // const. defines.
1485     if (rc == KTC_ERROR)
1486         errText = "KTC_ERROR";
1487     else if (rc == KTC_TOOBIG)
1488         errText = "KTC_TOOBIG";
1489     else if (rc == KTC_INVAL)
1490         errText = "KTC_INVAL";
1491     else if (rc == KTC_NOENT)
1492         errText = "KTC_NOENT";
1493     else if (rc == KTC_PIOCTLFAIL)
1494         errText = "KTC_PIOCTLFAIL";
1495     else if (rc == KTC_NOPIOCTL)
1496         errText = "KTC_NOPIOCTL";
1497     else if (rc == KTC_NOCELL)
1498         errText = "KTC_NOCELL";
1499     else if (rc == KTC_NOCM)
1500         errText = "KTC_NOCM: The service, Transarc AFS Daemon, most likely is not started!";
1501     else
1502         errText = "Unknown error!";
1503
1504     StringCbPrintfA(message, sizeof(message),
1505                     "%s\n(%s failed)", errText, FailedFunctionName);
1506     _report_cs1(KHERR_ERROR, L"%1!S!", _cptr(message));
1507     _resolve();
1508     return;
1509 }
1510
1511 DWORD
1512 GetServiceStatus(LPSTR lpszMachineName,
1513                  LPSTR lpszServiceName,
1514                  DWORD *lpdwCurrentState,
1515                  DWORD *lpdwWaitHint)
1516 {
1517     DWORD           hr               = NOERROR;
1518     SC_HANDLE       schSCManager     = NULL;
1519     SC_HANDLE       schService       = NULL;
1520     DWORD           fdwDesiredAccess = 0;
1521     SERVICE_STATUS  ssServiceStatus  = {0};
1522     BOOL            fRet             = FALSE;
1523
1524     *lpdwCurrentState = 0;
1525
1526     fdwDesiredAccess = GENERIC_READ;
1527
1528     schSCManager = OpenSCManagerA(lpszMachineName,
1529                                   NULL,
1530                                   fdwDesiredAccess);
1531
1532     if(schSCManager == NULL) {
1533         hr = GetLastError();
1534         goto cleanup;
1535     }
1536
1537     schService = OpenServiceA(schSCManager,
1538                               lpszServiceName,
1539                               fdwDesiredAccess);
1540
1541     if(schService == NULL) {
1542         hr = GetLastError();
1543         goto cleanup;
1544     }
1545
1546     fRet = QueryServiceStatus(schService,
1547                               &ssServiceStatus);
1548
1549     if(fRet == FALSE) {
1550         hr = GetLastError();
1551         goto cleanup;
1552     }
1553
1554     *lpdwCurrentState = ssServiceStatus.dwCurrentState;
1555     if (lpdwWaitHint)
1556         *lpdwWaitHint = ssServiceStatus.dwWaitHint;
1557 cleanup:
1558
1559     CloseServiceHandle(schService);
1560     CloseServiceHandle(schSCManager);
1561
1562     return(hr);
1563 }
1564
1565 DWORD ServiceControl(LPSTR lpszMachineName,
1566                      LPSTR lpszServiceName,
1567                      DWORD dwNewState) {
1568
1569     DWORD           hr               = NOERROR;
1570     SC_HANDLE       schSCManager     = NULL;
1571     SC_HANDLE       schService       = NULL;
1572     DWORD           fdwDesiredAccess = 0;
1573     SERVICE_STATUS  ssServiceStatus  = {0};
1574     BOOL            fRet             = FALSE;
1575     DWORD           dwCurrentState   = 0;
1576
1577     dwCurrentState = 0;
1578
1579     fdwDesiredAccess = GENERIC_READ;
1580
1581     schSCManager = OpenSCManagerA(lpszMachineName, NULL,
1582                                   fdwDesiredAccess);
1583
1584     if(schSCManager == NULL) {
1585         hr = GetLastError();
1586         goto cleanup;
1587     }
1588
1589     fdwDesiredAccess = GENERIC_READ | GENERIC_EXECUTE;
1590
1591     schService = OpenServiceA(schSCManager, lpszServiceName,
1592                               fdwDesiredAccess);
1593
1594     if(schService == NULL) {
1595         hr = GetLastError();
1596         goto cleanup;
1597     }
1598
1599     fRet = QueryServiceStatus(schService, &ssServiceStatus);
1600
1601     if(fRet == FALSE) {
1602         hr = GetLastError();
1603         goto cleanup;
1604     }
1605
1606     dwCurrentState = ssServiceStatus.dwCurrentState;
1607
1608     if (dwCurrentState == SERVICE_STOPPED &&
1609         dwNewState == SERVICE_RUNNING) {
1610
1611         fRet = StartService(schService, 0, NULL);
1612
1613         if (fRet == FALSE) {
1614             hr = GetLastError();
1615             goto cleanup;
1616         }
1617     }
1618
1619     if (dwCurrentState == SERVICE_RUNNING &&
1620         dwNewState == SERVICE_STOPPED) {
1621         fRet = ControlService(schService, SERVICE_CONTROL_STOP,
1622                               &ssServiceStatus);
1623
1624         if (fRet == FALSE) {
1625             hr = GetLastError();
1626             goto cleanup;
1627         }
1628     }
1629
1630 cleanup:
1631
1632     CloseServiceHandle(schService);
1633     CloseServiceHandle(schSCManager);
1634
1635     return(hr);
1636 }
1637
1638 khm_boolean
1639 afs_check_for_cell_realm_match(khm_handle identity, char * cell) {
1640     char local_cell[MAXCELLCHARS];
1641     wchar_t wrealm[MAXCELLCHARS];
1642     wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
1643     wchar_t * atsign;
1644     khm_size cb;
1645     char * realm;
1646     afs_conf_cell cellconfig;
1647     int rc;
1648
1649     ZeroMemory(local_cell, sizeof(local_cell));
1650     ZeroMemory(&cellconfig, sizeof(cellconfig));
1651
1652     rc = afs_get_cellconfig(cell, &cellconfig, local_cell);
1653     if (rc)
1654         return FALSE;
1655
1656     realm = afs_realm_of_cell(&cellconfig, FALSE);
1657     if (cellconfig.linkedCell)
1658         free(cellconfig.linkedCell);
1659     if (!realm[0])      /* referral; assume it matches */
1660         return TRUE;
1661
1662     AnsiStrToUnicode(wrealm, sizeof(wrealm), realm);
1663
1664     cb = sizeof(idname);
1665     idname[0] = L'\0';
1666     kcdb_identity_get_name(identity, idname, &cb);
1667
1668     atsign = wcschr(idname, L'@');
1669     if (atsign && atsign[1] && !_wcsicmp(atsign + 1, wrealm)) {
1670         return TRUE;
1671     } else {
1672         return FALSE;
1673     }
1674 }