windows-nim-afscred-referrals-two-20080105
[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
733     long        rc;
734     CREDENTIALS creds;
735     struct ktc_principal        aserver;
736     struct ktc_principal        aclient;
737     char        realm_of_user[REALM_SZ]; /* Kerberos realm of user */
738     char        realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
739     char        local_cell[MAXCELLCHARS+1];
740     char        Dmycell[MAXCELLCHARS+1];
741     struct ktc_token    atoken;
742     struct ktc_token    btoken;
743     afs_conf_cell       ak_cellconfig; /* General information about the cell */
744     char        RealmName[128];
745     char        CellName[128];
746     char        ServiceName[128];
747     khm_handle  confighandle = NULL;
748     khm_int32   supports_krb4 = (pkrb_get_tf_realm == NULL ? 0 : 1);
749     khm_int32   got524cred = 0;
750
751     /* signalling */
752     BOOL        bGotCreds = FALSE; /* got creds? */
753
754     if (tok_expiration)
755         *tok_expiration = (time_t) 0;
756
757     if (!afs_is_running()) {
758         _report_sr0(KHERR_WARNING, IDS_ERR_NOSERVICE);
759         return(0);
760     }
761
762     if ( !realm )   realm = "";
763     if ( !cell )    cell = "";
764     if ( !service ) service = "";
765
766     memset(RealmName, '\0', sizeof(RealmName));
767     memset(CellName, '\0', sizeof(CellName));
768     memset(ServiceName, '\0', sizeof(ServiceName));
769     memset(realm_of_user, '\0', sizeof(realm_of_user));
770     memset(realm_of_cell, '\0', sizeof(realm_of_cell));
771     memset(Dmycell, '\0', sizeof(Dmycell));
772
773     // NULL or empty cell returns information on local cell
774     if (cell && cell[0])
775         StringCbCopyA(Dmycell, sizeof(Dmycell), cell);
776
777     rc = afs_get_cellconfig(Dmycell, &ak_cellconfig, local_cell);
778     if (rc) {
779         _reportf(L"afs_get_cellconfig returns %ld", rc);
780
781         _report_sr2(KHERR_ERROR, IDS_ERR_CELLCONFIG, _cstr(Dmycell), _int32(rc));
782         _suggest_sr(IDS_ERR_CELLCONFIG_S, KHERR_SUGGEST_NONE);
783         _resolve();
784         return(rc);
785     }
786
787     StringCbCopyA(realm_of_cell, sizeof(realm_of_cell), 
788                   afs_realm_of_cell(&ak_cellconfig, FALSE));
789
790     if (strlen(service) == 0)
791         StringCbCopyA(ServiceName, sizeof(ServiceName), "afs");
792     else
793         StringCbCopyA(ServiceName, sizeof(ServiceName), service);
794
795     if (strlen(cell) == 0)
796         StringCbCopyA(CellName, sizeof(CellName), local_cell);
797     else
798         StringCbCopyA(CellName, sizeof(CellName), cell);
799
800     if (strlen(realm) == 0)
801         StringCbCopyA(RealmName, sizeof(RealmName), realm_of_cell);
802     else
803         StringCbCopyA(RealmName, sizeof(RealmName), realm);
804
805     memset(&creds, '\0', sizeof(creds));
806
807     /*** Kerberos 5 and 524 ***/
808
809     if (method == AFS_TOKEN_AUTO ||
810         method == AFS_TOKEN_KRB5 ||
811         method == AFS_TOKEN_KRB524) {
812
813         krb5_context   context = 0;
814         krb5_ccache    k5cc = 0;
815         krb5_creds     increds;
816         krb5_creds *   k5creds = 0;
817         krb5_error_code r;
818         krb5_principal client_principal = 0;
819         krb5_flags     flags = 0;
820
821         int         retry = 0;
822         int         len;
823         char        *p;
824
825         _reportf(L"Trying Kerberos 5");
826
827         if (!(r = khm_krb5_initialize(identity, &context, &k5cc))) {
828             int i;
829
830             memset((char *)&increds, 0, sizeof(increds));
831
832             pkrb5_cc_get_principal(context, k5cc, &client_principal);
833             i = krb5_princ_realm(context, client_principal)->length;
834             if (i > REALM_SZ-1) 
835                 i = REALM_SZ-1;
836             StringCchCopyNA(realm_of_user, ARRAYLENGTH(realm_of_user),
837                             krb5_princ_realm(context, client_principal)->data,
838                             i);
839         } else {
840             _reportf(L"khm_krb5_initialize returns code %d", r);
841             goto try_krb4;
842         }
843
844         /* First try Service/Cell@REALM */
845         if (r = pkrb5_build_principal(context, &increds.server,
846                                          (int) strlen(RealmName),
847                                          RealmName,
848                                          ServiceName,
849                                          CellName,
850                                          0)) {
851             _reportf(L"krb5_build_principal returns %d", r);
852             goto end_krb5;
853         }
854
855         increds.client = client_principal;
856         increds.times.endtime = 0;
857         /* Ask for DES since that is what V4 understands */
858         increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
859
860 #ifdef KRB5_TC_NOTICKET
861         flags = KRB5_TC_OPENCLOSE;
862         r = pkrb5_cc_set_flags(context, k5cc, flags);
863 #endif
864       retry_retcred:
865         r = pkrb5_get_credentials(context, 0, k5cc, &increds, &k5creds);
866         if ((r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
867               r == KRB5KRB_ERR_GENERIC /* Heimdal */) &&
868              !RealmName[0]) {
869             StringCbCopyA(RealmName, sizeof(RealmName), 
870                           afs_realm_of_cell(&ak_cellconfig, TRUE));
871
872             pkrb5_free_principal(context, increds.server);
873             r = pkrb5_build_principal(context, &increds.server,
874                                          (int) strlen(RealmName),
875                                          RealmName,
876                                          ServiceName,
877                                          CellName,
878                                          0);
879             if (r == 0)
880                 r = pkrb5_get_credentials(context, 0, k5cc, 
881                                           &increds, &k5creds);
882         }
883         if (r == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
884             r == KRB5KRB_ERR_GENERIC /* Heimdal */) {
885             /* Next try Service@REALM */
886             pkrb5_free_principal(context, increds.server);
887             r = pkrb5_build_principal(context, &increds.server,
888                                          (int) strlen(RealmName),
889                                          RealmName,
890                                          ServiceName,
891                                          0);
892             if (r == 0)
893                 r = pkrb5_get_credentials(context, 0, k5cc, 
894                                           &increds, &k5creds);
895         }
896
897         /* Check to make sure we received a valid ticket; if not remove it
898         * and try again.  Perhaps there are two service tickets for the
899         * same service in the ccache.
900         */
901         if (r == 0 && k5creds && k5creds->times.endtime < time(NULL)) {
902             pkrb5_cc_remove_cred(context, k5cc, 0, k5creds);
903             pkrb5_free_creds(context, k5creds);
904             k5creds = NULL;
905             goto retry_retcred;
906         }
907
908         if (r == 0 && strlen(RealmName) == 0) 
909             copy_realm_of_ticket(context, realm_of_cell, sizeof(realm_of_cell), k5creds);
910
911         pkrb5_free_principal(context, increds.server);
912         pkrb5_free_principal(context, client_principal);
913         client_principal = 0;
914 #ifdef KRB5_TC_NOTICKET
915         flags = KRB5_TC_OPENCLOSE | KRB5_TC_NOTICKET;
916         pkrb5_cc_set_flags(context, k5cc, flags);
917 #endif
918
919         (void) pkrb5_cc_close(context, k5cc);
920         k5cc = 0;
921
922         if (r) {
923             _reportf(L"Code %d while getting credentials", r);
924             k5creds = NULL;
925             goto end_krb5;
926         }
927
928         if ( k5creds->ticket.length > MAXKTCTICKETLEN ||
929              method == AFS_TOKEN_KRB524) {
930             goto try_krb524d;
931         }
932
933         /* This code inserts the entire K5 ticket into the token */
934
935         _reportf(L"Trying K5 SetToken");
936
937         memset(&aserver, '\0', sizeof(aserver));
938         StringCchCopyA(aserver.name, MAXKTCNAMELEN, ServiceName);
939         StringCchCopyA(aserver.cell, MAXKTCREALMLEN, CellName);
940
941         memset(&atoken, '\0', sizeof(atoken));
942         atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
943         atoken.startTime = k5creds->times.starttime;
944         atoken.endTime = k5creds->times.endtime;
945         memcpy(&atoken.sessionKey, 
946                k5creds->keyblock.contents, 
947                k5creds->keyblock.length);
948         atoken.ticketLen = k5creds->ticket.length;
949         memcpy(atoken.ticket, k5creds->ticket.data, atoken.ticketLen);
950
951         if (tok_expiration)
952             *tok_expiration = k5creds->times.endtime;
953
954     retry_gettoken5:
955         rc = ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient);
956         if (rc != 0 && rc != KTC_NOENT && rc != KTC_NOCELL) {
957             if ( rc == KTC_NOCM && retry < 20 ) {
958                 Sleep(500);
959                 retry++;
960                 goto retry_gettoken5;
961             }
962             goto try_krb524d;
963         }
964
965         if (atoken.kvno == btoken.kvno &&
966             atoken.ticketLen == btoken.ticketLen &&
967             !memcmp(&atoken.sessionKey, &btoken.sessionKey, 
968                     sizeof(atoken.sessionKey)) &&
969             !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) {
970
971             /* success */
972             if (k5creds && context)
973                 pkrb5_free_creds(context, k5creds);
974
975             if (context)
976                 pkrb5_free_context(context);
977
978             _reportf(L"Same token already exists");
979             
980             return 0;
981         }
982
983         // * Reset the "aclient" structure before we call ktc_SetToken.
984         // * This structure was first set by the ktc_GetToken call when
985         // * we were comparing whether identical tokens already existed.
986
987         len = min(k5creds->client->data[0].length,MAXKTCNAMELEN - 1);
988         StringCchCopyNA(aclient.name, MAXKTCNAMELEN,
989                         k5creds->client->data[0].data, len);
990
991         if ( k5creds->client->length > 1 ) {
992             StringCbCatA(aclient.name, sizeof(aclient.name), ".");
993             p = aclient.name + strlen(aclient.name);
994             len = (int) min(k5creds->client->data[1].length,
995                             MAXKTCNAMELEN - strlen(aclient.name) - 1);
996             StringCchCopyNA(p, MAXKTCNAMELEN - strlen(aclient.name),
997                             k5creds->client->data[1].data, len);
998         }
999
1000         aclient.instance[0] = '\0';
1001
1002         StringCbCopyA(aclient.cell, sizeof(aclient.cell), realm_of_cell);
1003
1004         StringCbCatA(aclient.name, sizeof(aclient.name), "@");
1005         p = aclient.name + strlen(aclient.name);
1006         len = (int) min(k5creds->client->realm.length,
1007                          MAXKTCNAMELEN - strlen(aclient.name) - 1);
1008         StringCchCopyNA(p, MAXKTCNAMELEN - strlen(aclient.name),
1009                         k5creds->client->realm.data, len);
1010
1011         ViceIDToUsername(aclient.name, realm_of_user, realm_of_cell, CellName, 
1012                          &aclient, &aserver, &atoken);
1013
1014         rc = ktc_SetToken(&aserver, &atoken, &aclient, 0);
1015         if (!rc) {
1016             /* success */
1017
1018             if (k5creds && context)
1019                 pkrb5_free_creds(context, k5creds);
1020
1021             if (context)
1022                 pkrb5_free_context(context);
1023             
1024             return 0;
1025         }
1026
1027         _reportf(L"SetToken returns code %d", rc);
1028
1029     try_krb524d:
1030
1031         _reportf(L"Trying Krb524");
1032
1033         if (pkrb524_convert_creds_kdc && 
1034             (method == AFS_TOKEN_AUTO || method == AFS_TOKEN_KRB524)) {
1035             /* This requires krb524d to be running with the KDC */
1036             r = pkrb524_convert_creds_kdc(context, k5creds, &creds);
1037             if (r) {
1038                 _reportf(L"Code %d while converting credentials", r);
1039                 goto end_krb5;
1040             }
1041             rc = KSUCCESS;
1042             got524cred = 1;
1043             bGotCreds = TRUE;
1044         }
1045
1046     end_krb5:
1047         if (client_principal)
1048             pkrb5_free_principal(context, client_principal);
1049
1050         if (k5creds && context)
1051             pkrb5_free_creds(context, k5creds);
1052
1053         if (context)
1054             pkrb5_free_context(context);
1055     }
1056
1057     /* Kerberos 4 */
1058  try_krb4:
1059
1060     if (supports_krb4) {
1061         kcdb_identity_get_config(identity, 0, &confighandle);
1062         khc_read_int32(confighandle, L"Krb4Cred\\Krb4NewCreds", &supports_krb4);
1063         khc_close_space(confighandle); 
1064     }
1065
1066     if (!supports_krb4)
1067         _reportf(L"Kerberos 4 not configured");
1068
1069     if (!bGotCreds && supports_krb4 && 
1070         (method == AFS_TOKEN_AUTO ||
1071          method == AFS_TOKEN_KRB4)) {
1072
1073         KTEXT_ST        ticket;
1074
1075         _reportf(L"Trying Kerberos 4");
1076
1077         if (!realm_of_user[0] ) {
1078             if ((rc = (*pkrb_get_tf_realm)((*ptkt_string)(), realm_of_user)) 
1079                 != KSUCCESS) {
1080                 /* can't determine realm of user */
1081                 _reportf(L"krb_get_tf_realm returns %d", rc);
1082                 goto end_krb4;
1083             }
1084         }
1085
1086         _reportf(L"Trying to find %S.%S@%S", ServiceName, CellName, RealmName);
1087         rc = (*pkrb_get_cred)(ServiceName, CellName, RealmName, &creds);
1088         if (rc == NO_TKT_FIL) {
1089             // if the problem is that we have no krb4 tickets
1090             // do not attempt to continue
1091             _reportf(L"krb_get_cred returns %d (no ticket file)", rc);
1092             goto end_krb4;
1093         }
1094
1095         if (rc != KSUCCESS) {
1096             _reportf(L"Trying to find %S@%S", ServiceName, RealmName);
1097             rc = (*pkrb_get_cred)(ServiceName, "", RealmName, &creds);
1098         }
1099
1100         if (rc != KSUCCESS) {
1101             _reportf(L"Trying to obtain new ticket");
1102             if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, 
1103                                      CellName, RealmName, 0))
1104                 == KSUCCESS) {
1105                 if ((rc = (*pkrb_get_cred)(ServiceName, CellName, 
1106                                            RealmName, &creds)) != KSUCCESS) {
1107                     goto end_krb4;
1108                 } else {
1109                     _reportf(L"Got %S.%S@%S", ServiceName, CellName, RealmName);
1110                 }
1111             } else if ((rc = (*pkrb_mk_req)(&ticket, ServiceName, 
1112                                             "", RealmName, 0))
1113                        == KSUCCESS) {
1114                 if ((rc = (*pkrb_get_cred)(ServiceName, "", 
1115                                            RealmName, &creds)) != KSUCCESS) {
1116                     goto end_krb4;
1117                 } else {
1118                     _reportf(L"Got %S@%S", ServiceName, RealmName);
1119                 }
1120             } else {
1121                 goto end_krb4;
1122             }
1123         }
1124
1125         bGotCreds = TRUE;
1126
1127     end_krb4:
1128         ;
1129     }
1130
1131     if (bGotCreds) {
1132
1133         memset(&aserver, '\0', sizeof(aserver));
1134         StringCchCopyA(aserver.name, MAXKTCNAMELEN, ServiceName);
1135         StringCchCopyA(aserver.cell, MAXKTCREALMLEN, CellName);
1136
1137         memset(&atoken, '\0', sizeof(atoken));
1138         atoken.kvno = (short)creds.kvno;
1139         atoken.startTime = creds.issue_date;
1140         atoken.endTime = (*pkrb_life_to_time)(creds.issue_date,creds.lifetime);
1141         memcpy(&atoken.sessionKey, creds.session, 8);
1142         atoken.ticketLen = creds.ticket_st.length;
1143         memcpy(atoken.ticket, creds.ticket_st.dat, atoken.ticketLen);
1144
1145         if (tok_expiration)
1146             *tok_expiration = atoken.endTime;
1147
1148         if (!(rc = ktc_GetToken(&aserver, &btoken, 
1149                                 sizeof(btoken), &aclient)) &&
1150             atoken.kvno == btoken.kvno &&
1151             atoken.ticketLen == btoken.ticketLen &&
1152             !memcmp(&atoken.sessionKey, &btoken.sessionKey, 
1153                     sizeof(atoken.sessionKey)) &&
1154             !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) {
1155
1156             /* success! */
1157             return(0);
1158         }
1159
1160         // Reset the "aclient" structure before we call ktc_SetToken.
1161         // This structure was first set by the ktc_GetToken call when
1162         // we were comparing whether identical tokens already existed.
1163
1164         StringCchCopyA(aclient.name, MAXKTCNAMELEN, creds.pname);
1165         if (creds.pinst[0]) {
1166             StringCchCatA(aclient.name, MAXKTCNAMELEN, ".");
1167             StringCchCatA(aclient.name, MAXKTCNAMELEN, creds.pinst);
1168         }
1169
1170         StringCbCopyA(aclient.instance, sizeof(aclient.instance), "");
1171
1172         StringCchCatA(aclient.name, MAXKTCNAMELEN, "@");
1173                 StringCchCatA(aclient.name, MAXKTCNAMELEN, got524cred ? realm_of_user : creds.realm);
1174
1175         StringCbCopyA(aclient.cell, sizeof(aclient.cell), CellName);
1176
1177         ViceIDToUsername(aclient.name, realm_of_user, realm_of_cell, CellName, 
1178                          &aclient, &aserver, &atoken);
1179
1180         if (rc = ktc_SetToken(&aserver, &atoken, &aclient, 0)) {
1181             afs_report_error(rc, "ktc_SetToken()");
1182             return(rc);
1183         }
1184     } else if (method == AFS_TOKEN_AUTO ||
1185                method >= AFS_TOKEN_USER) {
1186         /* we couldn't get a token using Krb5, Krb524 or Krb4, either
1187            because we couldn't get the necessary credentials or
1188            because the method was set to not use those.  Now we
1189            dispatch to any extensions to see if they have better
1190            luck. */
1191
1192         rc = !afs_ext_klog(method,
1193                            identity,
1194                            ServiceName,
1195                            CellName,
1196                            RealmName,
1197                            &ak_cellconfig,
1198                            LifeTime);
1199     } else {
1200         /* if the return code was not set, we should set it now.
1201            Otherwise we let the code go through. */
1202         if (!rc) {
1203             /* No tokens were obtained.  We should report something */
1204             _report_sr1(KHERR_ERROR, IDS_ERR_GENERAL,
1205                         _cptr(CellName));
1206             _resolve();
1207
1208             rc = KHM_ERROR_GENERAL;
1209         }
1210     }
1211
1212     return rc;
1213 }
1214
1215 /**************************************/
1216 /* afs_realm_of_cell():               */
1217 /**************************************/
1218 static char *
1219 afs_realm_of_cell(afs_conf_cell *cellconfig, BOOL referral_fallback)
1220 {
1221     char krbhst[MAX_HSTNM]="";
1222     static char krbrlm[REALM_SZ+1]="";
1223     krb5_context  ctx = 0;
1224     char ** realmlist=NULL;
1225     krb5_error_code r = 0;
1226
1227     if (!cellconfig)
1228         return 0;
1229
1230     if (referral_fallback) {
1231         char * p;
1232         p = strchr(cellconfig->hostName[0], '.');
1233         if (p++)
1234             StringCbCopyA(krbrlm, sizeof(krbrlm), p);
1235         else
1236             StringCbCopyA(krbrlm, sizeof(krbrlm), cellconfig->name);
1237 #if _MSC_VER >= 1400
1238         _strupr_s(krbrlm, sizeof(krbrlm));
1239 #else
1240         _strupr(krbrlm);
1241 #endif
1242     } else {
1243         if ( pkrb5_init_context ) {
1244             r = pkrb5_init_context(&ctx); 
1245             if ( !r )
1246                 r = pkrb5_get_host_realm(ctx, cellconfig->hostName[0], &realmlist);
1247             if ( !r && realmlist && realmlist[0] ) {
1248                 StringCbCopyA(krbrlm, sizeof(krbrlm), realmlist[0]);
1249                 pkrb5_free_host_realm(ctx, realmlist);
1250             }
1251             if (ctx)
1252                 pkrb5_free_context(ctx);
1253         }
1254
1255         if (r) {
1256             if (pkrb_get_krbhst && pkrb_realmofhost) {
1257                 StringCbCopyA(krbrlm, sizeof(krbrlm), 
1258                                (char *)(*pkrb_realmofhost)(cellconfig->hostName[0]));
1259                 if ((*pkrb_get_krbhst)(krbhst, krbrlm, 1) != KSUCCESS)
1260                     krbrlm[0] = '\0';
1261             }
1262
1263             if ( !krbrlm[0] ) {
1264                 char * p;
1265                 p = strchr(cellconfig->hostName[0], '.');
1266                 if (p++)
1267                     StringCbCopyA(krbrlm, sizeof(krbrlm), p);
1268                 else
1269                     StringCbCopyA(krbrlm, sizeof(krbrlm), cellconfig->name);
1270 #if _MSC_VER >= 1400
1271                 _strupr_s(krbrlm, sizeof(krbrlm));
1272 #else
1273                 _strupr(krbrlm);
1274 #endif
1275             }
1276         }
1277     }
1278     return(krbrlm);
1279 }
1280
1281 /**************************************/
1282 /* afs_get_cellconfig():                  */
1283 /**************************************/
1284 static int 
1285 afs_get_cellconfig(char *cell, afs_conf_cell *cellconfig, char *local_cell)
1286 {
1287     int rc;
1288     int ttl;
1289
1290     local_cell[0] = (char)0;
1291     memset(cellconfig, 0, sizeof(*cellconfig));
1292
1293     cellconfig->cbsize = sizeof(*cellconfig);
1294
1295     /* WIN32: cm_GetRootCellName(local_cell) - NOTE: no way to get max chars */
1296     if (rc = cm_GetRootCellName(local_cell)) {
1297         return(rc);
1298     }
1299
1300     if (strlen(cell) == 0)
1301         StringCbCopyA(cell, (MAXCELLCHARS+1) * sizeof(char), local_cell);
1302
1303     /* WIN32: cm_SearchCellFile(cell, pcallback, pdata) */
1304     StringCbCopyA(cellconfig->name, (MAXCELLCHARS+1) * sizeof(char), cell);
1305
1306     rc = cm_SearchCellFile(cell, NULL, afs_get_cellconfig_callback, 
1307                            (void*)cellconfig);
1308     if(rc)
1309         rc = cm_SearchCellByDNS(cell, NULL, &ttl, 
1310                                 afs_get_cellconfig_callback, 
1311                                 (void*) cellconfig);
1312
1313     return rc;
1314 }
1315
1316 /**************************************/
1317 /* afs_get_cellconfig_callback():          */
1318 /**************************************/
1319 static long 
1320 afs_get_cellconfig_callback(void *cellconfig, 
1321                             struct sockaddr_in *addrp, 
1322                             char *namep)
1323 {
1324     afs_conf_cell *cc = (afs_conf_cell *)cellconfig;
1325
1326     cc->hostAddr[cc->numServers] = *addrp;
1327     StringCbCopyA(cc->hostName[cc->numServers], 
1328                   sizeof(cc->hostName[0]), namep);
1329     cc->numServers++;
1330     return(0);
1331 }
1332
1333
1334 /**************************************/
1335 /* afs_report_error():           */
1336 /**************************************/
1337 void
1338 afs_report_error(LONG rc, LPCSTR FailedFunctionName)
1339 {
1340     char message[256];
1341     const char *errText; 
1342
1343     // Using AFS defines as error messages for now, until Transarc 
1344     // gets back to me with "string" translations of each of these 
1345     // const. defines. 
1346     if (rc == KTC_ERROR)
1347         errText = "KTC_ERROR";
1348     else if (rc == KTC_TOOBIG)
1349         errText = "KTC_TOOBIG";
1350     else if (rc == KTC_INVAL)
1351         errText = "KTC_INVAL";
1352     else if (rc == KTC_NOENT)
1353         errText = "KTC_NOENT";
1354     else if (rc == KTC_PIOCTLFAIL)
1355         errText = "KTC_PIOCTLFAIL";
1356     else if (rc == KTC_NOPIOCTL)
1357         errText = "KTC_NOPIOCTL";
1358     else if (rc == KTC_NOCELL)
1359         errText = "KTC_NOCELL";
1360     else if (rc == KTC_NOCM)
1361         errText = "KTC_NOCM: The service, Transarc AFS Daemon, most likely is not started!";
1362     else
1363         errText = "Unknown error!";
1364
1365     StringCbPrintfA(message, sizeof(message), 
1366                     "%s\n(%s failed)", errText, FailedFunctionName);
1367     _report_cs1(KHERR_ERROR, L"%1!S!", _cptr(message));
1368     _resolve();
1369     return;
1370 }
1371
1372 DWORD 
1373 GetServiceStatus(LPSTR lpszMachineName, 
1374                  LPSTR lpszServiceName, 
1375                  DWORD *lpdwCurrentState,
1376                  DWORD *lpdwWaitHint) 
1377
1378     DWORD           hr               = NOERROR; 
1379     SC_HANDLE       schSCManager     = NULL; 
1380     SC_HANDLE       schService       = NULL; 
1381     DWORD           fdwDesiredAccess = 0; 
1382     SERVICE_STATUS  ssServiceStatus  = {0}; 
1383     BOOL            fRet             = FALSE; 
1384
1385     *lpdwCurrentState = 0; 
1386  
1387     fdwDesiredAccess = GENERIC_READ; 
1388  
1389     schSCManager = OpenSCManagerA(lpszMachineName,  
1390                                   NULL,
1391                                   fdwDesiredAccess); 
1392  
1393     if(schSCManager == NULL) { 
1394         hr = GetLastError();
1395         goto cleanup; 
1396     } 
1397  
1398     schService = OpenServiceA(schSCManager,
1399                               lpszServiceName,
1400                               fdwDesiredAccess);
1401  
1402     if(schService == NULL) { 
1403         hr = GetLastError();
1404         goto cleanup; 
1405     } 
1406  
1407     fRet = QueryServiceStatus(schService,
1408                               &ssServiceStatus); 
1409  
1410     if(fRet == FALSE) { 
1411         hr = GetLastError(); 
1412         goto cleanup; 
1413     } 
1414  
1415     *lpdwCurrentState = ssServiceStatus.dwCurrentState; 
1416     if (lpdwWaitHint)
1417         *lpdwWaitHint = ssServiceStatus.dwWaitHint;
1418 cleanup: 
1419  
1420     CloseServiceHandle(schService); 
1421     CloseServiceHandle(schSCManager); 
1422  
1423     return(hr); 
1424
1425
1426 DWORD ServiceControl(LPSTR lpszMachineName, 
1427                      LPSTR lpszServiceName,
1428                      DWORD dwNewState) {
1429
1430     DWORD           hr               = NOERROR; 
1431     SC_HANDLE       schSCManager     = NULL; 
1432     SC_HANDLE       schService       = NULL; 
1433     DWORD           fdwDesiredAccess = 0; 
1434     SERVICE_STATUS  ssServiceStatus  = {0}; 
1435     BOOL            fRet             = FALSE; 
1436     DWORD           dwCurrentState   = 0;
1437
1438     dwCurrentState = 0; 
1439  
1440     fdwDesiredAccess = GENERIC_READ;
1441  
1442     schSCManager = OpenSCManagerA(lpszMachineName, NULL, 
1443                                   fdwDesiredAccess); 
1444  
1445     if(schSCManager == NULL) {
1446         hr = GetLastError();
1447         goto cleanup; 
1448     }
1449
1450     fdwDesiredAccess = GENERIC_READ | GENERIC_EXECUTE;
1451
1452     schService = OpenServiceA(schSCManager, lpszServiceName,
1453                               fdwDesiredAccess);
1454  
1455     if(schService == NULL) {
1456         hr = GetLastError();
1457         goto cleanup; 
1458     } 
1459  
1460     fRet = QueryServiceStatus(schService, &ssServiceStatus);
1461  
1462     if(fRet == FALSE) {
1463         hr = GetLastError(); 
1464         goto cleanup; 
1465     } 
1466  
1467     dwCurrentState = ssServiceStatus.dwCurrentState; 
1468
1469     if (dwCurrentState == SERVICE_STOPPED &&
1470         dwNewState == SERVICE_RUNNING) {
1471
1472         fRet = StartService(schService, 0, NULL);
1473
1474         if (fRet == FALSE) {
1475             hr = GetLastError();
1476             goto cleanup;
1477         }
1478     }
1479
1480     if (dwCurrentState == SERVICE_RUNNING &&
1481         dwNewState == SERVICE_STOPPED) {
1482         fRet = ControlService(schService, SERVICE_CONTROL_STOP, 
1483                               &ssServiceStatus);
1484
1485         if (fRet == FALSE) {
1486             hr = GetLastError();
1487             goto cleanup;
1488         }
1489     }
1490  
1491 cleanup: 
1492  
1493     CloseServiceHandle(schService); 
1494     CloseServiceHandle(schSCManager); 
1495  
1496     return(hr); 
1497 }
1498
1499 khm_boolean
1500 afs_check_for_cell_realm_match(khm_handle identity, char * cell) {
1501     char local_cell[MAXCELLCHARS];
1502     wchar_t wrealm[MAXCELLCHARS];
1503     wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
1504     wchar_t * atsign;
1505     khm_size cb;
1506     char * realm;
1507     afs_conf_cell cellconfig;
1508     int rc;
1509
1510     ZeroMemory(local_cell, sizeof(local_cell));
1511
1512     rc = afs_get_cellconfig(cell, &cellconfig, local_cell);
1513     if (rc)
1514         return FALSE;
1515
1516     realm = afs_realm_of_cell(&cellconfig, FALSE);
1517     if (realm == NULL)
1518         return FALSE;
1519
1520     AnsiStrToUnicode(wrealm, sizeof(wrealm), realm);
1521
1522     cb = sizeof(idname);
1523     idname[0] = L'\0';
1524     kcdb_identity_get_name(identity, idname, &cb);
1525
1526     atsign = wcschr(idname, L'@');
1527     if (atsign && atsign[1] && !_wcsicmp(atsign + 1, wrealm)) {
1528         return TRUE;
1529     } else {
1530         return FALSE;
1531     }
1532 }