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