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