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