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