2 * Copyright (c) 2005,2006,2007, 2008 Secure Endpoints Inc.
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:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
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
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)
34 #include<krb5common.h>
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 *);
45 afs_is_running(void) {
51 if (GetServiceStatus(NULL
, TRANSARCAFSDAEMON
,
52 &CurrentState
, NULL
) != NOERROR
)
54 if (CurrentState
!= SERVICE_RUNNING
)
65 if (!afs_is_running())
68 rc
= ktc_ForgetAllTokens();
74 afs_unlog_cred(khm_handle cred
)
77 struct ktc_principal princ
;
79 wchar_t name
[KCDB_MAXCCH_NAME
];
81 if (!afs_is_running())
84 cbbuf
= sizeof(princ
);
85 if(KHM_FAILED(kcdb_cred_get_attr(cred
, afs_attr_server_princ
,
86 NULL
, &princ
, &cbbuf
)))
89 afs_princ_to_string(&princ
, name
, sizeof(name
));
91 _report_cs1(KHERR_INFO
, L
"Destroying token %1!s!",
95 rc
= ktc_ForgetToken(&princ
);
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
104 afs_princ_to_string(struct ktc_principal
* p
,
112 l
= AnsiStrToUnicode(wbuf
, sizeof(wbuf
), p
->name
);
115 rv
= FAILED(StringCbCopy(buf
, cbbuf
, wbuf
));
117 StringCbCat(buf
, cbbuf
, L
".");
118 if((l
= AnsiStrToUnicode(wbuf
, sizeof(wbuf
), p
->instance
)) > 0) {
120 rv
= rv
|| FAILED(StringCbCat(buf
, cbbuf
, wbuf
));
126 rv
= rv
|| FAILED(StringCbCat(buf
, cbbuf
, L
"@"));
127 if((l
= AnsiStrToUnicode(wbuf
, sizeof(wbuf
), p
->cell
)) > 0) {
129 rv
= rv
|| FAILED(StringCbCat(buf
, cbbuf
, wbuf
));
139 afs_list_tokens(void)
143 kcdb_credset_flush(afs_credset
);
144 r
= afs_list_tokens_internal();
145 kcdb_credset_collect(NULL
, afs_credset
, NULL
, afs_credtype_id
, NULL
);
148 afs_icon_set_state(AFSICON_REPORT_TOKENS
, afs_credset
);
149 } else if (r
== -1) {
150 afs_icon_set_state(AFSICON_SERVICE_STOPPED
, NULL
);
152 afs_icon_set_state(AFSICON_SERVICE_ERROR
, NULL
);
158 /* is the credential provided an AFS token and is it from the
160 static khm_int32 KHMAPI
161 afs_filter_by_cell(khm_handle cred
, khm_int32 flags
, void * rock
)
163 wchar_t wcell
[MAXCELLCHARS
];
168 tcell
= (wchar_t *) rock
;
170 if(KHM_FAILED(kcdb_cred_get_type(cred
, &type
)) ||
171 type
!= afs_credtype_id
)
174 cbsize
= sizeof(wcell
);
175 if(KHM_FAILED(kcdb_cred_get_attr(cred
, afs_attr_cell
,
176 NULL
, wcell
, &cbsize
)))
179 if(wcscmp(wcell
, tcell
))
185 struct token_filter_data
{
190 afs_filter_for_token(khm_handle cred
, khm_int32 flags
, void * rock
) {
191 struct token_filter_data
* pdata
;
192 wchar_t ccell
[MAXCELLCHARS
];
196 pdata
= (struct token_filter_data
*) rock
;
198 if (KHM_FAILED(kcdb_cred_get_type(cred
, &ctype
)) ||
199 ctype
!= afs_credtype_id
)
205 if (KHM_FAILED(kcdb_cred_get_attr(cred
, afs_attr_cell
,
209 _wcsicmp(ccell
, pdata
->cell
))
217 afs_find_token(khm_handle credset
, wchar_t * cell
) {
218 struct token_filter_data fdata
;
219 khm_handle cred
= NULL
;
223 if (KHM_FAILED(kcdb_credset_find_filtered(credset
,
225 afs_filter_for_token
,
234 static khm_int32 KHMAPI
235 afs_filter_krb5_tkt(khm_handle cred
, khm_int32 flags
, void * rock
)
237 wchar_t cname
[KCDB_CRED_MAXCCH_NAME
];
240 wchar_t * t
, *tkt_cell
;
243 tcell
= (wchar_t *) rock
;
245 if(KHM_FAILED(kcdb_cred_get_type(cred
, &type
)) ||
246 type
!= krb5_credtype_id
)
249 cbsize
= sizeof(cname
);
250 if (KHM_FAILED(kcdb_cred_get_name(cred
, cname
, &cbsize
)))
253 if (!wcsncmp(cname
, L
"afs/", 4)) {
255 tkt_cell
= cname
+ 4;
257 t
= wcschr(tkt_cell
, L
'@');
262 } else if (!wcsncmp(cname
, L
"afs@", 4)) {
264 tkt_cell
= cname
+ 4;
270 if (_wcsicmp(tcell
, tkt_cell
))
276 static khm_int32 KHMAPI
277 afs_filter_krb4_tkt(khm_handle cred
, khm_int32 flags
, void * rock
)
279 wchar_t cname
[KCDB_CRED_MAXCCH_NAME
];
282 wchar_t * t
, *tkt_cell
;
285 tcell
= (wchar_t *) rock
;
287 if(KHM_FAILED(kcdb_cred_get_type(cred
, &type
)) ||
288 type
!= krb4_credtype_id
)
291 cbsize
= sizeof(cname
);
292 if (KHM_FAILED(kcdb_cred_get_name(cred
, cname
, &cbsize
)))
295 if (!wcsncmp(cname
, L
"afs.", 4)) {
297 tkt_cell
= cname
+ 4;
299 t
= wcschr(tkt_cell
, L
'@');
304 } else if (!wcsncmp(cname
, L
"afs@", 4)) {
306 tkt_cell
= cname
+ 4;
312 if (_wcsicmp(tcell
, tkt_cell
))
318 /* collects all AFS tokens to the root credential set using the
319 generic afs_credset credential set
322 afs_list_tokens_internal(void)
324 struct ktc_principal aserver
;
325 struct ktc_principal aclient
;
326 struct ktc_token atoken
;
331 wchar_t location
[256];
336 khm_handle ident
= NULL
;
337 khm_handle cred
= NULL
;
338 afs_tk_method method
;
342 if (!afs_is_running())
345 kcdb_credset_flush(afs_credset
);
347 LoadString(hResModule
, IDS_DEF_LOCATION
, location
, ARRAYLENGTH(location
));
353 memset(&aserver
, 0, sizeof(aserver
));
354 if (rc
= ktc_ListTokens(cellNum
, &cellNum
, &aserver
))
363 memset(&atoken
, '\0', sizeof(atoken
));
364 if (rc
= ktc_GetToken(&aserver
, &atoken
, sizeof(atoken
), &aclient
))
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 */
377 if(atoken
.kvno
== RXKAD_TKT_TYPE_KERBEROS_V5
) {
378 krb5_context ctx
= 0;
381 krb5_error_code code
;
384 code
= khm_krb5_initialize(&ctx
, &cc
);
388 k5c
= (krb5_creds
*) atoken
.ticket
;
390 code
= pkrb5_unparse_name(ctx
, k5c
->client
, &princ
);
394 MultiByteToWideChar(CP_ACP
, 0, princ
, strlen(princ
), idname
, sizeof(idname
)/sizeof(idname
[0]));
396 pkrb5_free_unparsed_name(ctx
, princ
);
402 method
= AFS_TOKEN_AUTO
;
404 afs_princ_to_string(&aclient
, idname
, sizeof(idname
));
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.
410 Note that, we use another heuristic to find out which
411 identity to associate the token with.
415 The assumption here is that the principal for the token is
418 if realm != cell : principal looks like user@realm@cell
419 if realm == cell : principal looks like user@realm
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. */
430 ats
= wcschr(idname
, L
'@');
431 if(ats
&& (ats
= wcschr(ats
+ 1, L
'@')))
435 afs_princ_to_string(&aserver
, crname
, sizeof(crname
));
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.
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.
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.
457 cell
= wcschr(crname
, L
'@');
468 if(KHM_SUCCEEDED(kcdb_credset_find_filtered(NULL
, -1,
474 kcdb_cred_get_identity(c
, &ident
);
476 kcdb_cred_get_attr(c
, afs_attr_method
, NULL
,
478 kcdb_cred_release(c
);
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
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
496 If such a Krb5 ticket is found, use the identity of that
497 credential as the identity of the AFS token.
500 if (ident
== NULL
&& cell
!= NULL
) {
503 if(KHM_SUCCEEDED(kcdb_credset_find_filtered(NULL
, -1,
507 kcdb_cred_get_identity(c
, &ident
);
508 /* this could be Krb5 or Krb524, so we leave method at
510 method
= AFS_TOKEN_AUTO
;
511 kcdb_cred_release(c
);
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
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.
527 If such a ticket is found, then use the identity of that
528 credential as the identity of the AFS token.
530 if (ident
== NULL
&& cell
!= NULL
) {
533 if (krb4_credtype_id
< 0) {
534 kcdb_credtype_get_id(KRB4_CREDTYPE_NAME
,
538 if (krb4_credtype_id
>= 0 &&
539 KHM_SUCCEEDED(kcdb_credset_find_filtered(NULL
, -1,
544 kcdb_cred_get_identity(c
, &ident
);
545 kcdb_cred_release(c
);
546 method
= AFS_TOKEN_KRB4
;
551 /* Finally, we allow any extension plugins to give this a shot */
552 if (ident
== NULL
&& cell
!= NULL
) {
553 afs_ext_resolve_token(cell
,
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
];
568 cb
= sizeof(tidname
);
570 if (KHM_SUCCEEDED(khc_open_space(csp_afscred
,
573 if (KHM_SUCCEEDED(khc_read_string(h_cellmap
,
577 kcdb_identity_create(tidname
,
578 KCDB_IDENT_FLAG_CREATE
,
581 khc_close_space(h_cellmap
);
585 /* all else failed */
587 if(KHM_FAILED(kcdb_identity_create(idname
,
588 KCDB_IDENT_FLAG_CREATE
,
593 if(KHM_FAILED(kcdb_cred_create(crname
, ident
, afs_credtype_id
, &cred
)))
596 kcdb_cred_set_attr(cred
, afs_attr_method
, &method
, sizeof(method
));
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
));
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
));
610 kcdb_cred_set_attr(cred
, afs_attr_cell
, cell
, (khm_size
)KCDB_CBSIZE_AUTO
);
613 kcdb_cred_set_attr(cred
, KCDB_ATTR_LOCATION
,
614 location
, (khm_size
)KCDB_CBSIZE_AUTO
);
616 kcdb_credset_add_cred(afs_credset
, cred
, -1);
618 /* both these calls are NULL pointer safe */
619 kcdb_cred_release(cred
);
621 kcdb_identity_release(ident
);
627 kcdb_identity_release(ident
);
629 kcdb_cred_release(cred
);
635 #define ALLOW_REGISTER 1
637 ViceIDToUsername(char *username
,
641 struct ktc_principal
*aclient
,
642 struct ktc_principal
*aserver
,
643 struct ktc_token
*atoken
)
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 */
650 #ifdef ALLOW_REGISTER
652 #endif /* ALLOW_REGISTER */
654 if (confname
[0] == '\0') {
655 StringCbCopyA(confname
, sizeof(confname
), AFSDIR_CLIENT_ETC_DIRPATH
);
658 StringCbCopyA(lastcell
, sizeof(lastcell
), aserver
->cell
);
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
);
667 #ifdef AFS_ID_TO_NAME
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.
677 #endif /* AFS_ID_TO_NAME */
679 * This code is taken from cklog -- it lets people
680 * automatically register with the ptserver in foreign cells
683 /* copy the username because pr_CreateUser will lowercase it */
684 StringCbCopyA(username_copy
, BUFSIZ
, username
);
686 #ifdef ALLOW_REGISTER
688 if (viceId
!= ANONYMOUSID
) {
689 #else /* ALLOW_REGISTER */
690 if ((status
== 0) && (viceId
!= ANONYMOUSID
))
691 #endif /* ALLOW_REGISTER */
693 #ifdef AFS_ID_TO_NAME
694 StringCchPrintfA(username
, BUFSIZ
, "%s (AFS ID %d)", username_copy
, (int) viceId
);
695 #endif /* AFS_ID_TO_NAME */
697 #ifdef ALLOW_REGISTER
698 } else if (strcmp(realm_of_user
, realm_of_cell
) != 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))
705 if (status
= pr_Initialize(1L, confname
, aserver
->cell
))
707 status
= pr_CreateUser(username
, &id
);
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 */
715 #endif /* ALLOW_REGISTER */
721 copy_realm_of_ticket(krb5_context context
, char * dest
, size_t destlen
, krb5_creds
*v5cred
) {
722 krb5_error_code code
;
726 code
= pkrb5_decode_ticket(&v5cred
->ticket
, &ticket
);
728 len
= krb5_princ_realm(context
, ticket
->server
)->length
;
729 if (len
> destlen
- 1)
732 StringCbCopyA(dest
, len
, krb5_princ_realm(context
, ticket
->server
)->data
);
734 pkrb5_free_ticket(context
, ticket
);
739 afs_klog(khm_handle identity
,
744 afs_tk_method method
,
745 time_t * tok_expiration
,
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 */
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;
767 BOOL bGotCreds
= FALSE
; /* got creds? */
770 *tok_expiration
= (time_t) 0;
772 if (!afs_is_running()) {
773 _report_sr0(KHERR_WARNING
, IDS_ERR_NOSERVICE
);
777 if ( !realm
) realm
= "";
778 if ( !cell
) cell
= "";
779 if ( !service
) service
= "";
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
));
789 // NULL or empty cell returns information on local cell
791 StringCbCopyA(Dmycell
, sizeof(Dmycell
), cell
);
793 rc
= afs_get_cellconfig(Dmycell
, &ak_cellconfig
, local_cell
);
795 _reportf(L
"afs_get_cellconfig returns %ld", rc
);
797 _report_sr2(KHERR_ERROR
, IDS_ERR_CELLCONFIG
, _cstr(Dmycell
), _int32(rc
));
798 _suggest_sr(IDS_ERR_CELLCONFIG_S
, KHERR_SUGGEST_NONE
);
803 if (linkedCell
&& ak_cellconfig
.linkedCell
)
804 StringCbCopyA(linkedCell
, MAXCELLCHARS
,
805 ak_cellconfig
.linkedCell
);
807 StringCbCopyA(realm_of_cell
, sizeof(realm_of_cell
),
808 afs_realm_of_cell(&ak_cellconfig
, FALSE
));
810 if (strlen(service
) == 0)
811 StringCbCopyA(ServiceName
, sizeof(ServiceName
), "afs");
813 StringCbCopyA(ServiceName
, sizeof(ServiceName
), service
);
815 if (strlen(cell
) == 0)
816 StringCbCopyA(CellName
, sizeof(CellName
), local_cell
);
818 StringCbCopyA(CellName
, sizeof(CellName
), cell
);
820 if (strlen(realm
) == 0)
821 StringCbCopyA(RealmName
, sizeof(RealmName
), realm_of_cell
);
823 StringCbCopyA(RealmName
, sizeof(RealmName
), realm
);
825 memset(&creds
, '\0', sizeof(creds
));
827 /*** Kerberos 5 and 524 ***/
829 if (method
== AFS_TOKEN_AUTO
||
830 method
== AFS_TOKEN_KRB5
||
831 method
== AFS_TOKEN_KRB524
) {
833 krb5_context context
= 0;
834 krb5_ccache k5cc
= 0;
836 krb5_creds
* k5creds
= 0;
838 krb5_principal client_principal
= 0;
839 krb5_flags flags
= 0;
845 _reportf(L
"Trying Kerberos 5");
847 if (!(r
= khm_krb5_initialize(identity
, &context
, &k5cc
))) {
850 memset(&increds
, 0, sizeof(increds
));
852 r
= pkrb5_cc_get_principal(context
, k5cc
, &client_principal
);
854 i
= krb5_princ_realm(context
, client_principal
)->length
;
855 if (i
> MAXKTCREALMLEN
-1)
856 i
= MAXKTCREALMLEN
-1;
857 StringCchCopyNA(realm_of_user
, ARRAYLENGTH(realm_of_user
),
858 krb5_princ_realm(context
, client_principal
)->data
,
861 _reportf(L
"krb5_cc_get_principal returns code %d", r
);
865 _reportf(L
"khm_krb5_initialize returns code %d", r
);
869 increds
.client
= client_principal
;
870 increds
.times
.endtime
= 0;
871 /* Ask for DES since that is what V4 understands */
872 increds
.keyblock
.enctype
= ENCTYPE_DES_CBC_CRC
;
874 #ifdef KRB5_TC_NOTICKET
875 flags
= KRB5_TC_OPENCLOSE
;
876 r
= pkrb5_cc_set_flags(context
, k5cc
, flags
);
878 if (strlen(realm
) != 0) {
880 /* First try Service/Cell@REALM */
881 if (r
= pkrb5_build_principal(context
, &increds
.server
,
887 _reportf(L
"krb5_build_principal returns %d", r
);
891 r
= pkrb5_get_credentials(context
, 0, k5cc
, &increds
, &k5creds
);
892 if (r
== KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
||
893 r
== KRB5_ERR_HOST_REALM_UNKNOWN
||
894 r
== KRB5KRB_ERR_GENERIC
/* Heimdal */) {
895 /* Next try Service@REALM */
896 pkrb5_free_principal(context
, increds
.server
);
897 r
= pkrb5_build_principal(context
, &increds
.server
,
903 r
= pkrb5_get_credentials(context
, 0, k5cc
,
907 /* Check to make sure we received a valid ticket; if not remove it
908 * and try again. Perhaps there are two service tickets for the
909 * same service in the ccache.
911 if (r
== 0 && k5creds
&& k5creds
->times
.endtime
< time(NULL
)) {
912 pkrb5_free_principal(context
, increds
.server
);
913 pkrb5_cc_remove_cred(context
, k5cc
, 0, k5creds
);
914 pkrb5_free_creds(context
, k5creds
);
916 goto retry_retcred_1
;
920 /* First try Service/Cell@_CLIENT_REALM */
921 if (r
= pkrb5_build_principal(context
, &increds
.server
,
922 (int) strlen(realm_of_user
),
927 _reportf(L
"krb5_build_principal returns %d", r
);
931 r
= pkrb5_get_credentials(context
, 0, k5cc
, &increds
, &k5creds
);
933 /* the user realm is a valid cell realm */
934 StringCbCopyA(realm_of_cell
, sizeof(realm_of_cell
), realm_of_user
);
936 if (r
== KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
||
937 r
== KRB5_ERR_HOST_REALM_UNKNOWN
||
938 r
== KRB5KRB_ERR_GENERIC
/* Heimdal */) {
939 pkrb5_free_principal(context
, increds
.server
);
940 r
= pkrb5_build_principal(context
, &increds
.server
,
941 (int) strlen(realm_of_cell
),
947 r
= pkrb5_get_credentials(context
, 0, k5cc
,
950 if ((r
== KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
||
951 r
== KRB5_ERR_HOST_REALM_UNKNOWN
||
952 r
== KRB5KRB_ERR_GENERIC
/* Heimdal */) &&
953 strlen(realm_of_cell
) == 0) {
954 StringCbCopyA(realm_of_cell
, sizeof(realm_of_cell
),
955 afs_realm_of_cell(&ak_cellconfig
, TRUE
));
957 pkrb5_free_principal(context
, increds
.server
);
958 r
= pkrb5_build_principal(context
, &increds
.server
,
959 (int) strlen(realm_of_cell
),
965 r
= pkrb5_get_credentials(context
, 0, k5cc
,
968 if (r
== KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
||
969 r
== KRB5_ERR_HOST_REALM_UNKNOWN
||
970 r
== KRB5KRB_ERR_GENERIC
/* Heimdal */) {
971 /* Next try Service@REALM */
972 StringCbCopyA(realm_of_cell
, sizeof(realm_of_cell
),
973 afs_realm_of_cell(&ak_cellconfig
, FALSE
));
975 pkrb5_free_principal(context
, increds
.server
);
976 r
= pkrb5_build_principal(context
, &increds
.server
,
977 (int) strlen(realm_of_cell
),
982 r
= pkrb5_get_credentials(context
, 0, k5cc
,
985 if ((r
== KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
||
986 r
== KRB5_ERR_HOST_REALM_UNKNOWN
||
987 r
== KRB5KRB_ERR_GENERIC
/* Heimdal */) &&
988 strlen(realm_of_cell
) == 0) {
989 /* Next try Service@REALM */
990 StringCbCopyA(realm_of_cell
, sizeof(realm_of_cell
),
991 afs_realm_of_cell(&ak_cellconfig
, TRUE
));
993 pkrb5_free_principal(context
, increds
.server
);
994 r
= pkrb5_build_principal(context
, &increds
.server
,
995 (int) strlen(realm_of_cell
),
1000 r
= pkrb5_get_credentials(context
, 0, k5cc
,
1001 &increds
, &k5creds
);
1004 if (r
== 0 && strlen(realm_of_cell
) == 0)
1005 copy_realm_of_ticket(context
, realm_of_cell
, sizeof(realm_of_cell
), k5creds
);
1007 /* Check to make sure we received a valid ticket; if not remove it
1008 * and try again. Perhaps there are two service tickets for the
1009 * same service in the ccache.
1011 if (r
== 0 && k5creds
&& k5creds
->times
.endtime
< time(NULL
)) {
1012 pkrb5_free_principal(context
, increds
.server
);
1013 pkrb5_cc_remove_cred(context
, k5cc
, 0, k5creds
);
1014 pkrb5_free_creds(context
, k5creds
);
1016 goto retry_retcred_2
;
1020 pkrb5_free_principal(context
, increds
.server
);
1021 pkrb5_free_principal(context
, client_principal
);
1022 client_principal
= 0;
1023 #ifdef KRB5_TC_NOTICKET
1024 flags
= KRB5_TC_OPENCLOSE
| KRB5_TC_NOTICKET
;
1025 pkrb5_cc_set_flags(context
, k5cc
, flags
);
1028 (void) pkrb5_cc_close(context
, k5cc
);
1032 _reportf(L
"Code %d while getting credentials", r
);
1037 if ( k5creds
->ticket
.length
> MAXKTCTICKETLEN
||
1038 method
== AFS_TOKEN_KRB524
) {
1042 /* This code inserts the entire K5 ticket into the token */
1044 _reportf(L
"Trying K5 SetToken");
1046 memset(&aserver
, '\0', sizeof(aserver
));
1047 StringCchCopyA(aserver
.name
, MAXKTCNAMELEN
, ServiceName
);
1048 StringCchCopyA(aserver
.cell
, MAXKTCREALMLEN
, CellName
);
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
->keyblock
.contents
,
1056 k5creds
->keyblock
.length
);
1057 atoken
.ticketLen
= k5creds
->ticket
.length
;
1058 memcpy(atoken
.ticket
, k5creds
->ticket
.data
, atoken
.ticketLen
);
1061 *tok_expiration
= k5creds
->times
.endtime
;
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 ) {
1069 goto retry_gettoken5
;
1074 if (atoken
.kvno
== btoken
.kvno
&&
1075 atoken
.ticketLen
== btoken
.ticketLen
&&
1076 !memcmp(&atoken
.sessionKey
, &btoken
.sessionKey
,
1077 sizeof(atoken
.sessionKey
)) &&
1078 !memcmp(atoken
.ticket
, btoken
.ticket
, atoken
.ticketLen
)) {
1081 if (k5creds
&& context
)
1082 pkrb5_free_creds(context
, k5creds
);
1085 pkrb5_free_context(context
);
1087 _reportf(L
"Same token already exists");
1093 // * Reset the "aclient" structure before we call ktc_SetToken.
1094 // * This structure was first set by the ktc_GetToken call when
1095 // * we were comparing whether identical tokens already existed.
1097 len
= min(k5creds
->client
->data
[0].length
,MAXKTCNAMELEN
- 1);
1098 StringCchCopyNA(aclient
.name
, MAXKTCNAMELEN
,
1099 k5creds
->client
->data
[0].data
, len
);
1101 if ( k5creds
->client
->length
> 1 ) {
1102 StringCbCatA(aclient
.name
, sizeof(aclient
.name
), ".");
1103 p
= aclient
.name
+ strlen(aclient
.name
);
1104 len
= (int) min(k5creds
->client
->data
[1].length
,
1105 MAXKTCNAMELEN
- strlen(aclient
.name
) - 1);
1106 StringCchCopyNA(p
, MAXKTCNAMELEN
- strlen(aclient
.name
),
1107 k5creds
->client
->data
[1].data
, len
);
1110 aclient
.instance
[0] = '\0';
1112 StringCbCopyA(aclient
.cell
, sizeof(aclient
.cell
), realm_of_cell
);
1114 StringCbCatA(aclient
.name
, sizeof(aclient
.name
), "@");
1115 p
= aclient
.name
+ strlen(aclient
.name
);
1116 len
= (int) min(k5creds
->client
->realm
.length
,
1117 MAXKTCNAMELEN
- strlen(aclient
.name
) - 1);
1118 StringCchCopyNA(p
, MAXKTCNAMELEN
- strlen(aclient
.name
),
1119 k5creds
->client
->realm
.data
, len
);
1121 ViceIDToUsername(aclient
.name
, realm_of_user
, realm_of_cell
, CellName
,
1122 &aclient
, &aserver
, &atoken
);
1124 rc
= ktc_SetToken(&aserver
, &atoken
, &aclient
, 0);
1128 if (k5creds
&& context
)
1129 pkrb5_free_creds(context
, k5creds
);
1132 pkrb5_free_context(context
);
1137 _reportf(L
"SetToken returns code %d", rc
);
1141 _reportf(L
"Trying Krb524");
1143 if (pkrb524_convert_creds_kdc
&&
1144 (method
== AFS_TOKEN_AUTO
|| method
== AFS_TOKEN_KRB524
)) {
1145 /* This requires krb524d to be running with the KDC */
1146 r
= pkrb524_convert_creds_kdc(context
, k5creds
, &creds
);
1148 _reportf(L
"Code %d while converting credentials", r
);
1157 if (client_principal
)
1158 pkrb5_free_principal(context
, client_principal
);
1160 if (k5creds
&& context
)
1161 pkrb5_free_creds(context
, k5creds
);
1164 pkrb5_free_context(context
);
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
);
1177 _reportf(L
"Kerberos 4 not configured");
1179 if (!bGotCreds
&& supports_krb4
&&
1180 strlen(RealmName
) < REALM_SZ
&&
1181 (method
== AFS_TOKEN_AUTO
||
1182 method
== AFS_TOKEN_KRB4
)) {
1186 _reportf(L
"Trying Kerberos 4");
1188 if (!realm_of_user
[0] ) {
1189 if ((rc
= (*pkrb_get_tf_realm
)((*ptkt_string
)(), realm_of_user
))
1191 /* can't determine realm of user */
1192 _reportf(L
"krb_get_tf_realm returns %d", rc
);
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
);
1206 if (rc
!= KSUCCESS
) {
1207 _reportf(L
"Trying to find %S@%S", ServiceName
, RealmName
);
1208 rc
= (*pkrb_get_cred
)(ServiceName
, "", RealmName
, &creds
);
1211 if (rc
!= KSUCCESS
) {
1212 _reportf(L
"Trying to obtain new ticket");
1213 if ((rc
= (*pkrb_mk_req
)(&ticket
, ServiceName
,
1214 CellName
, RealmName
, 0))
1216 if ((rc
= (*pkrb_get_cred
)(ServiceName
, CellName
,
1217 RealmName
, &creds
)) != KSUCCESS
) {
1220 _reportf(L
"Got %S.%S@%S", ServiceName
, CellName
, RealmName
);
1222 } else if ((rc
= (*pkrb_mk_req
)(&ticket
, ServiceName
,
1225 if ((rc
= (*pkrb_get_cred
)(ServiceName
, "",
1226 RealmName
, &creds
)) != KSUCCESS
) {
1229 _reportf(L
"Got %S@%S", ServiceName
, RealmName
);
1244 memset(&aserver
, '\0', sizeof(aserver
));
1245 StringCchCopyA(aserver
.name
, MAXKTCNAMELEN
, ServiceName
);
1246 StringCchCopyA(aserver
.cell
, MAXKTCREALMLEN
, CellName
);
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
);
1257 *tok_expiration
= atoken
.endTime
;
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
)) {
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.
1276 StringCchCopyA(aclient
.name
, MAXKTCNAMELEN
, creds
.pname
);
1277 if (creds
.pinst
[0]) {
1278 StringCchCatA(aclient
.name
, MAXKTCNAMELEN
, ".");
1279 StringCchCatA(aclient
.name
, MAXKTCNAMELEN
, creds
.pinst
);
1282 StringCbCopyA(aclient
.instance
, sizeof(aclient
.instance
), "");
1284 StringCchCatA(aclient
.name
, MAXKTCNAMELEN
, "@");
1285 StringCchCatA(aclient
.name
, MAXKTCNAMELEN
, got524cred
? realm_of_user
: creds
.realm
);
1287 StringCbCopyA(aclient
.cell
, sizeof(aclient
.cell
), CellName
);
1289 ViceIDToUsername(aclient
.name
, realm_of_user
, realm_of_cell
, CellName
,
1290 &aclient
, &aserver
, &atoken
);
1292 if (rc
= ktc_SetToken(&aserver
, &atoken
, &aclient
, 0)) {
1293 afs_report_error(rc
, "ktc_SetToken()");
1296 } else if (method
== AFS_TOKEN_AUTO
||
1297 method
>= AFS_TOKEN_USER
) {
1298 /* we couldn't get a token using Krb5, Krb524 or Krb4, either
1299 because we couldn't get the necessary credentials or
1300 because the method was set to not use those. Now we
1301 dispatch to any extensions to see if they have better
1304 rc
= !afs_ext_klog(method
,
1312 /* if the return code was not set, we should set it now.
1313 Otherwise we let the code go through. */
1315 /* No tokens were obtained. We should report something */
1316 _report_sr1(KHERR_ERROR
, IDS_ERR_GENERAL
,
1320 rc
= KHM_ERROR_GENERAL
;
1325 if (ak_cellconfig
.linkedCell
)
1326 free(ak_cellconfig
.linkedCell
);
1331 /**************************************/
1332 /* afs_realm_of_cell(): */
1333 /**************************************/
1335 afs_realm_of_cell(afs_conf_cell
*cellconfig
, BOOL referral_fallback
)
1337 char krbhst
[MAX_HSTNM
]="";
1338 static char krbrlm
[MAXKTCREALMLEN
+1]="";
1339 krb5_context ctx
= 0;
1340 char ** realmlist
=NULL
;
1341 krb5_error_code r
= 0;
1346 if (referral_fallback
) {
1348 p
= strchr(cellconfig
->hostName
[0], '.');
1350 StringCbCopyA(krbrlm
, sizeof(krbrlm
), p
);
1352 StringCbCopyA(krbrlm
, sizeof(krbrlm
), cellconfig
->name
);
1353 #if _MSC_VER >= 1400
1354 _strupr_s(krbrlm
, sizeof(krbrlm
));
1359 if ( pkrb5_init_context
) {
1360 r
= pkrb5_init_context(&ctx
);
1362 r
= pkrb5_get_host_realm(ctx
, cellconfig
->hostName
[0], &realmlist
);
1363 if ( !r
&& realmlist
&& realmlist
[0] ) {
1364 StringCbCopyA(krbrlm
, sizeof(krbrlm
), realmlist
[0]);
1365 pkrb5_free_host_realm(ctx
, realmlist
);
1368 pkrb5_free_context(ctx
);
1372 if (pkrb_get_krbhst
&& pkrb_realmofhost
) {
1373 StringCbCopyA(krbrlm
, sizeof(krbrlm
),
1374 (char *)(*pkrb_realmofhost
)(cellconfig
->hostName
[0]));
1375 if ((*pkrb_get_krbhst
)(krbhst
, krbrlm
, 1) != KSUCCESS
)
1381 p
= strchr(cellconfig
->hostName
[0], '.');
1383 StringCbCopyA(krbrlm
, sizeof(krbrlm
), p
);
1385 StringCbCopyA(krbrlm
, sizeof(krbrlm
), cellconfig
->name
);
1386 #if _MSC_VER >= 1400
1387 _strupr_s(krbrlm
, sizeof(krbrlm
));
1397 /**************************************/
1398 /* afs_get_cellconfig(): */
1399 /**************************************/
1401 afs_get_cellconfig(char *cell
, afs_conf_cell
*cellconfig
, char *local_cell
)
1405 char linkedCell
[MAXCELLCHARS
]="";
1407 local_cell
[0] = (char)0;
1408 memset(cellconfig
, 0, sizeof(*cellconfig
));
1410 cellconfig
->cbsize
= sizeof(*cellconfig
);
1412 /* WIN32: cm_GetRootCellName(local_cell) - NOTE: no way to get max chars */
1413 if (rc
= cm_GetRootCellName(local_cell
)) {
1417 if (strlen(cell
) == 0)
1418 StringCbCopyA(cell
, (MAXCELLCHARS
+1) * sizeof(char), local_cell
);
1420 StringCbCopyA(cellconfig
->name
, (MAXCELLCHARS
+1) * sizeof(char), cell
);
1422 rc
= cm_SearchCellRegistry(1, cell
, NULL
, linkedCell
,
1423 afs_get_cellconfig_callback
, (void*) cellconfig
);
1424 if (rc
&& rc
!= CM_ERROR_FORCE_DNS_LOOKUP
)
1425 rc
= cm_SearchCellFileEx(cell
, NULL
, linkedCell
, afs_get_cellconfig_callback
,
1428 rc
= cm_SearchCellByDNS(cell
, NULL
, &ttl
,
1429 afs_get_cellconfig_callback
,
1430 (void*) cellconfig
);
1433 cellconfig
->linkedCell
= _strdup(linkedCell
);
1438 /**************************************/
1439 /* afs_get_cellconfig_callback(): */
1440 /**************************************/
1442 afs_get_cellconfig_callback(void *cellconfig
,
1443 struct sockaddr_in
*addrp
,
1445 unsigned short ipRank
)
1447 afs_conf_cell
*cc
= (afs_conf_cell
*)cellconfig
;
1449 cc
->hostAddr
[cc
->numServers
] = *addrp
;
1450 StringCbCopyA(cc
->hostName
[cc
->numServers
],
1451 sizeof(cc
->hostName
[0]), namep
);
1457 /**************************************/
1458 /* afs_report_error(): */
1459 /**************************************/
1461 afs_report_error(LONG rc
, LPCSTR FailedFunctionName
)
1464 const char *errText
;
1466 // Using AFS defines as error messages for now, until Transarc
1467 // gets back to me with "string" translations of each of these
1469 if (rc
== KTC_ERROR
)
1470 errText
= "KTC_ERROR";
1471 else if (rc
== KTC_TOOBIG
)
1472 errText
= "KTC_TOOBIG";
1473 else if (rc
== KTC_INVAL
)
1474 errText
= "KTC_INVAL";
1475 else if (rc
== KTC_NOENT
)
1476 errText
= "KTC_NOENT";
1477 else if (rc
== KTC_PIOCTLFAIL
)
1478 errText
= "KTC_PIOCTLFAIL";
1479 else if (rc
== KTC_NOPIOCTL
)
1480 errText
= "KTC_NOPIOCTL";
1481 else if (rc
== KTC_NOCELL
)
1482 errText
= "KTC_NOCELL";
1483 else if (rc
== KTC_NOCM
)
1484 errText
= "KTC_NOCM: The service, Transarc AFS Daemon, most likely is not started!";
1486 errText
= "Unknown error!";
1488 StringCbPrintfA(message
, sizeof(message
),
1489 "%s\n(%s failed)", errText
, FailedFunctionName
);
1490 _report_cs1(KHERR_ERROR
, L
"%1!S!", _cptr(message
));
1496 GetServiceStatus(LPSTR lpszMachineName
,
1497 LPSTR lpszServiceName
,
1498 DWORD
*lpdwCurrentState
,
1499 DWORD
*lpdwWaitHint
)
1502 SC_HANDLE schSCManager
= NULL
;
1503 SC_HANDLE schService
= NULL
;
1504 DWORD fdwDesiredAccess
= 0;
1505 SERVICE_STATUS ssServiceStatus
= {0};
1508 *lpdwCurrentState
= 0;
1510 fdwDesiredAccess
= GENERIC_READ
;
1512 schSCManager
= OpenSCManagerA(lpszMachineName
,
1516 if(schSCManager
== NULL
) {
1517 hr
= GetLastError();
1521 schService
= OpenServiceA(schSCManager
,
1525 if(schService
== NULL
) {
1526 hr
= GetLastError();
1530 fRet
= QueryServiceStatus(schService
,
1534 hr
= GetLastError();
1538 *lpdwCurrentState
= ssServiceStatus
.dwCurrentState
;
1540 *lpdwWaitHint
= ssServiceStatus
.dwWaitHint
;
1543 CloseServiceHandle(schService
);
1544 CloseServiceHandle(schSCManager
);
1549 DWORD
ServiceControl(LPSTR lpszMachineName
,
1550 LPSTR lpszServiceName
,
1554 SC_HANDLE schSCManager
= NULL
;
1555 SC_HANDLE schService
= NULL
;
1556 DWORD fdwDesiredAccess
= 0;
1557 SERVICE_STATUS ssServiceStatus
= {0};
1559 DWORD dwCurrentState
= 0;
1563 fdwDesiredAccess
= GENERIC_READ
;
1565 schSCManager
= OpenSCManagerA(lpszMachineName
, NULL
,
1568 if(schSCManager
== NULL
) {
1569 hr
= GetLastError();
1573 fdwDesiredAccess
= GENERIC_READ
| GENERIC_EXECUTE
;
1575 schService
= OpenServiceA(schSCManager
, lpszServiceName
,
1578 if(schService
== NULL
) {
1579 hr
= GetLastError();
1583 fRet
= QueryServiceStatus(schService
, &ssServiceStatus
);
1586 hr
= GetLastError();
1590 dwCurrentState
= ssServiceStatus
.dwCurrentState
;
1592 if (dwCurrentState
== SERVICE_STOPPED
&&
1593 dwNewState
== SERVICE_RUNNING
) {
1595 fRet
= StartService(schService
, 0, NULL
);
1597 if (fRet
== FALSE
) {
1598 hr
= GetLastError();
1603 if (dwCurrentState
== SERVICE_RUNNING
&&
1604 dwNewState
== SERVICE_STOPPED
) {
1605 fRet
= ControlService(schService
, SERVICE_CONTROL_STOP
,
1608 if (fRet
== FALSE
) {
1609 hr
= GetLastError();
1616 CloseServiceHandle(schService
);
1617 CloseServiceHandle(schSCManager
);
1623 afs_check_for_cell_realm_match(khm_handle identity
, char * cell
) {
1624 char local_cell
[MAXCELLCHARS
];
1625 wchar_t wrealm
[MAXCELLCHARS
];
1626 wchar_t idname
[KCDB_IDENT_MAXCCH_NAME
];
1630 afs_conf_cell cellconfig
;
1633 ZeroMemory(local_cell
, sizeof(local_cell
));
1634 ZeroMemory(&cellconfig
, sizeof(cellconfig
));
1636 rc
= afs_get_cellconfig(cell
, &cellconfig
, local_cell
);
1640 realm
= afs_realm_of_cell(&cellconfig
, FALSE
);
1641 if (cellconfig
.linkedCell
)
1642 free(cellconfig
.linkedCell
);
1643 if (!realm
[0]) /* referral; assume it matches */
1646 AnsiStrToUnicode(wrealm
, sizeof(wrealm
), realm
);
1648 cb
= sizeof(idname
);
1650 kcdb_identity_get_name(identity
, idname
, &cb
);
1652 atsign
= wcschr(idname
, L
'@');
1653 if (atsign
&& atsign
[1] && !_wcsicmp(atsign
+ 1, wrealm
)) {