12780aee203bdd08309460b265e2d61892c248fe
[openafs.git] / src / auth / ktc_nt.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* ticket caching code */
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14
15 RCSID
16     ("$Header$");
17
18 #include <afs/stds.h>
19 #include <stdio.h>
20 #include <afs/pthread_glock.h>
21 #include <sys/types.h>
22 #include <ctype.h>
23 #include <sys/stat.h>
24 #include <signal.h>
25 #include <errno.h>
26 #include <rpc.h>
27 #include <afs/smb_iocons.h>
28 #include <afs/pioctl_nt.h>
29 #include "afs/afsrpc.h"
30 #include <afs/vice.h>
31 #include "auth.h"
32 #include <afs/afsutil.h>
33
34
35 /* Forward declarations for local token cache. */
36 static int SetLocalToken(struct ktc_principal *aserver,
37                          struct ktc_token *atoken,
38                          struct ktc_principal *aclient, afs_int32 flags);
39 static int GetLocalToken(struct ktc_principal *aserver,
40                          struct ktc_token *atoken, int atokenLen,
41                          struct ktc_principal *aclient);
42 static int ForgetLocalTokens();
43 static int ForgetOneLocalToken(struct ktc_principal *aserver);
44
45
46 static char AFSConfigKeyName[] =
47     "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters";
48
49 /*
50  * Support for RPC's to send and receive session keys
51  *
52  * Session keys must not be sent and received in the clear.  We have no
53  * way to piggyback encryption on SMB, so we use a separate RPC, using
54  * packet privacy (when available).  In SetToken, the RPC is done first;
55  * in GetToken, the pioctl is done first.
56  */
57
58 char rpcErr[256];
59
60 void __RPC_FAR *__RPC_USER
61 midl_user_allocate(size_t cBytes)
62 {
63     return ((void __RPC_FAR *)malloc(cBytes));
64 }
65
66 void __RPC_USER
67 midl_user_free(void __RPC_FAR * p)
68 {
69     free(p);
70 }
71
72 /*
73  * Determine the server name to be used in the RPC binding.  If it is
74  * the same as the client (i.e. standalone, non-gateway), NULL can be
75  * used, so it is not necessary to call gethostbyname().
76  */
77 void
78 getservername(char **snp, unsigned int snSize)
79 {
80     HKEY parmKey;
81     long code;
82
83     code =
84         RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE,
85                      &parmKey);
86     if (code != ERROR_SUCCESS)
87         goto nogateway;
88     code = RegQueryValueEx(parmKey, "Gateway", NULL, NULL, *snp, &snSize);
89     RegCloseKey(parmKey);
90     if (code == ERROR_SUCCESS)
91         return;
92   nogateway:
93     /* No gateway name in registry; use ourself */
94     *snp = NULL;
95 }
96
97 RPC_STATUS
98 send_key(afs_uuid_t uuid, char sessionKey[8])
99 {
100     RPC_STATUS status;
101     char *stringBinding = NULL;
102     ULONG authnLevel, authnSvc;
103     char serverName[256];
104     char *serverNamep = serverName;
105     char encrypt[32];
106     BOOL encryptionOff = FALSE;
107     char protseq[32];
108
109     /* Encryption on by default */
110     if (GetEnvironmentVariable("AFS_RPC_ENCRYPT", encrypt, sizeof(encrypt)))
111         if (!strcmpi(encrypt, "OFF"))
112             encryptionOff = TRUE;
113
114     /* Protocol sequence is named pipe by default */
115     if (!GetEnvironmentVariable("AFS_RPC_PROTSEQ", protseq, sizeof(protseq)))
116         strcpy(protseq, "ncacn_np");
117
118     /* Server name */
119     getservername(&serverNamep, sizeof(serverName));
120
121     status = RpcStringBindingCompose("",        /* obj uuid */
122                                      protseq, serverNamep, "",  /* endpoint */
123                                      "",        /* protocol options */
124                                      &stringBinding);
125     if (status != RPC_S_OK)
126         goto cleanup;
127
128     status = RpcBindingFromStringBinding(stringBinding, &hAfsHandle);
129     if (status != RPC_S_OK)
130         goto cleanup;
131
132     /*
133      * On Windows 95/98, we must resolve the binding before calling
134      * SetAuthInfo.  On Windows NT, we don't have to resolve yet,
135      * but it does no harm.
136      */
137     status = RpcEpResolveBinding(hAfsHandle, afsrpc_v1_0_c_ifspec);
138     if (status != RPC_S_OK)
139         goto cleanup;
140
141     if (encryptionOff) {
142         authnLevel = RPC_C_AUTHN_LEVEL_NONE;
143         authnSvc = RPC_C_AUTHN_WINNT;
144     } else {
145         authnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
146         authnSvc = RPC_C_AUTHN_WINNT;
147     }
148
149     status =
150         RpcBindingSetAuthInfo(hAfsHandle, NULL, authnLevel, authnSvc, NULL,
151                               RPC_C_AUTHZ_NONE);
152     if (status != RPC_S_OK)
153         goto cleanup;
154
155     RpcTryExcept {
156         status = AFSRPC_SetToken(uuid, sessionKey);
157     }
158     RpcExcept(1) {
159         status = RpcExceptionCode();
160     }
161     RpcEndExcept cleanup:if (stringBinding)
162           RpcStringFree(&stringBinding);
163
164     if (hAfsHandle != NULL)
165         RpcBindingFree(&hAfsHandle);
166
167     return status;
168 }
169
170 RPC_STATUS
171 receive_key(afs_uuid_t uuid, char sessionKey[8])
172 {
173     RPC_STATUS status;
174     char *stringBinding = NULL;
175     ULONG authnLevel, authnSvc;
176     char serverName[256];
177     char *serverNamep = serverName;
178     char encrypt[32];
179     BOOL encryptionOff = FALSE;
180     char protseq[32];
181
182     /* Encryption on by default */
183     if (GetEnvironmentVariable("AFS_RPC_ENCRYPT", encrypt, sizeof(encrypt)))
184         if (!strcmpi(encrypt, "OFF"))
185             encryptionOff = TRUE;
186
187     /* Protocol sequence is named pipe by default */
188     if (!GetEnvironmentVariable("AFS_RPC_PROTSEQ", protseq, sizeof(protseq)))
189         strcpy(protseq, "ncacn_np");
190
191     /* Server name */
192     getservername(&serverNamep, sizeof(serverName));
193
194     status = RpcStringBindingCompose("",        /* obj uuid */
195                                      protseq, serverNamep, "",  /* endpoint */
196                                      "",        /* protocol options */
197                                      &stringBinding);
198     if (status != RPC_S_OK)
199         goto cleanup;
200
201     status = RpcBindingFromStringBinding(stringBinding, &hAfsHandle);
202     if (status != RPC_S_OK)
203         goto cleanup;
204
205     /*
206      * On Windows 95/98, we must resolve the binding before calling
207      * SetAuthInfo.  On Windows NT, we don't have to resolve yet,
208      * but it does no harm.
209      */
210     status = RpcEpResolveBinding(hAfsHandle, afsrpc_v1_0_c_ifspec);
211     if (status != RPC_S_OK)
212         goto cleanup;
213
214     if (encryptionOff) {
215         authnLevel = RPC_C_AUTHN_LEVEL_NONE;
216         authnSvc = RPC_C_AUTHN_WINNT;
217     } else {
218         authnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
219         authnSvc = RPC_C_AUTHN_WINNT;
220     }
221
222     status =
223         RpcBindingSetAuthInfo(hAfsHandle, NULL, authnLevel, authnSvc, NULL,
224                               RPC_C_AUTHZ_NONE);
225     if (status != RPC_S_OK)
226         goto cleanup;
227
228     RpcTryExcept {
229         status = AFSRPC_GetToken(uuid, sessionKey);
230     }
231     RpcExcept(1) {
232         status = RpcExceptionCode();
233     }
234     RpcEndExcept cleanup:if (stringBinding)
235           RpcStringFree(&stringBinding);
236
237     if (hAfsHandle != NULL)
238         RpcBindingFree(&hAfsHandle);
239
240     return status;
241 }
242
243 int
244 ktc_SetToken(struct ktc_principal *server, struct ktc_token *token,
245              struct ktc_principal *client, int flags)
246 {
247     struct ViceIoctl iob;
248     char tbuffer[1024];
249     char *tp;
250     struct ClearToken ct;
251     int temp;
252     int code;
253     RPC_STATUS status;
254     afs_uuid_t uuid;
255
256     if (token->ticketLen < MINKTCTICKETLEN
257         || token->ticketLen > MAXKTCTICKETLEN)
258         return KTC_INVAL;
259
260     if (strcmp(server->name, "afs")) {
261         return SetLocalToken(server, token, client, flags);
262     }
263
264     tp = tbuffer;
265
266     /* ticket length */
267     memcpy(tp, &token->ticketLen, sizeof(token->ticketLen));
268     tp += sizeof(&token->ticketLen);
269
270     /* ticket */
271     memcpy(tp, token->ticket, token->ticketLen);
272     tp += token->ticketLen;
273
274     /* clear token */
275     ct.AuthHandle = token->kvno;
276     /*
277      * Instead of sending the session key in the clear, we zero it,
278      * and send it later, via RPC, encrypted.
279      */
280 #ifndef AFS_WIN95_ENV
281     /*
282      * memcpy(ct.HandShakeKey, &token->sessionKey, sizeof(token->sessionKey));
283      */
284     memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
285 #else
286     memcpy(ct.HandShakeKey, &token->sessionKey, sizeof(token->sessionKey));
287 #endif
288     ct.BeginTimestamp = token->startTime;
289     ct.EndTimestamp = token->endTime;
290     if (ct.BeginTimestamp == 0)
291         ct.BeginTimestamp = 1;
292
293     /* We don't know from Vice ID's yet */
294     ct.ViceId = 37;             /* XXX */
295     if (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1)
296         ct.BeginTimestamp++;    /* force lifetime to be even */
297
298     /* size of clear token */
299     temp = sizeof(struct ClearToken);
300     memcpy(tp, &temp, sizeof(temp));
301     tp += sizeof(temp);
302
303     /* clear token itself */
304     memcpy(tp, &ct, sizeof(ct));
305     tp += sizeof(ct);
306
307     /* flags; on NT there is no setpag flag, but there is an
308      * integrated logon flag */
309     temp = ((flags & AFS_SETTOK_LOGON) ? PIOCTL_LOGON : 0);
310     memcpy(tp, &temp, sizeof(temp));
311     tp += sizeof(temp);
312
313     /* cell name */
314     temp = strlen(server->cell);
315     if (temp >= MAXKTCREALMLEN)
316         return KTC_INVAL;
317     strcpy(tp, server->cell);
318     tp += temp + 1;
319
320     /* user name */
321     temp = strlen(client->name);
322     if (temp >= MAXKTCNAMELEN)
323         return KTC_INVAL;
324     strcpy(tp, client->name);
325     tp += temp + 1;
326
327     /* we need the SMB user name to associate the tokens with in the
328      * integrated logon case. */
329     if (flags & AFS_SETTOK_LOGON) {
330         if (client->smbname == NULL)
331             temp = 0;
332         else
333             temp = strlen(client->smbname);
334         if (temp == 0 || temp >= MAXKTCNAMELEN)
335             return KTC_INVAL;
336         strcpy(tp, client->smbname);
337         tp += temp + 1;
338     }
339
340     /* uuid */
341     status = UuidCreate((UUID *) & uuid);
342     memcpy(tp, &uuid, sizeof(uuid));
343     tp += sizeof(uuid);
344
345 #ifndef AFS_WIN95_ENV
346     /* RPC to send session key */
347     status = send_key(uuid, token->sessionKey.data);
348     if (status != RPC_S_OK) {
349         if (status == 1)
350             strcpy(rpcErr, "RPC failure in AFS gateway");
351         else
352             DceErrorInqText(status, rpcErr);
353         if (status == RPC_S_SERVER_UNAVAILABLE
354             || status == EPT_S_NOT_REGISTERED)
355             return KTC_NOCMRPC;
356         else
357             return KTC_RPC;
358     }
359 #endif /* AFS_WIN95_ENV */
360
361     /* set up for pioctl */
362     iob.in = tbuffer;
363     iob.in_size = tp - tbuffer;
364     iob.out = tbuffer;
365     iob.out_size = sizeof(tbuffer);
366
367     code = pioctl(0, VIOCSETTOK, &iob, 0);
368     if (code) {
369         if (code == -1) {
370             if (errno == ESRCH)
371                 return KTC_NOCELL;
372             else if (errno == ENODEV)
373                 return KTC_NOCM;
374             else if (errno == EINVAL)
375                 return KTC_INVAL;
376             else
377                 return KTC_PIOCTLFAIL;
378         } else
379             return KTC_PIOCTLFAIL;
380     }
381
382     return 0;
383 }
384
385 int
386 ktc_GetToken(struct ktc_principal *server, struct ktc_token *token,
387              int tokenLen, struct ktc_principal *client)
388 {
389     struct ViceIoctl iob;
390     char tbuffer[1024];
391     char *tp, *cp;
392     char *ticketP;
393     int ticketLen, temp;
394     struct ClearToken ct;
395     char *cellName;
396     int cellNameSize;
397     int maxLen;
398     int code;
399     RPC_STATUS status;
400     afs_uuid_t uuid;
401
402     tp = tbuffer;
403
404     /* check to see if the user is requesting tokens for a principal
405      * other than afs.  If so, check the local token cache.
406      */
407     if (strcmp(server->name, "afs")) {
408         return GetLocalToken(server, token, tokenLen, client);
409     }
410
411     /* cell name */
412     strcpy(tp, server->cell);
413     tp += strlen(server->cell) + 1;
414
415     /* uuid */
416     status = UuidCreate((UUID *) & uuid);
417     memcpy(tp, &uuid, sizeof(uuid));
418     tp += sizeof(uuid);
419
420     iob.in = tbuffer;
421     iob.in_size = tp - tbuffer;
422     iob.out = tbuffer;
423     iob.out_size = sizeof(tbuffer);
424
425     code = pioctl(0, VIOCNEWGETTOK, &iob, 0);
426     if (code) {
427         if (code == -1) {
428             if (errno == ESRCH)
429                 return KTC_NOCELL;
430             else if (errno == ENODEV)
431                 return KTC_NOCM;
432             else if (errno == EINVAL)
433                 return KTC_INVAL;
434             else if (errno == EDOM)
435                 return KTC_NOENT;
436             else
437                 return KTC_PIOCTLFAIL;
438         } else
439             return KTC_PIOCTLFAIL;
440     }
441 #ifndef AFS_WIN95_ENV           /* get rid of RPC for win95 build */
442     /* RPC to receive session key */
443     status = receive_key(uuid, token->sessionKey.data);
444     if (status != RPC_S_OK) {
445         if (status == 1)
446             strcpy(rpcErr, "RPC failure in AFS gateway");
447         else
448             DceErrorInqText(status, rpcErr);
449         if (status == RPC_S_SERVER_UNAVAILABLE
450             || status == EPT_S_NOT_REGISTERED)
451             return KTC_NOCMRPC;
452         else
453             return KTC_RPC;
454     }
455 #endif /* AFS_WIN95_ENV */
456
457     cp = tbuffer;
458
459     /* ticket length */
460     memcpy(&ticketLen, cp, sizeof(ticketLen));
461     cp += sizeof(ticketLen);
462
463     /* remember where ticket is and skip over it */
464     ticketP = cp;
465     cp += ticketLen;
466
467     /* size of clear token */
468     memcpy(&temp, cp, sizeof(temp));
469     cp += sizeof(temp);
470     if (temp != sizeof(ct))
471         return KTC_ERROR;
472
473     /* clear token */
474     memcpy(&ct, cp, temp);
475     cp += temp;
476
477     /* skip over primary flag */
478     cp += sizeof(temp);
479
480     /* remember cell name and skip over it */
481     cellName = cp;
482     cellNameSize = strlen(cp);
483     cp += cellNameSize + 1;
484
485     /* user name is here */
486
487     /* check that ticket will fit */
488     maxLen = tokenLen - sizeof(struct ktc_token) + MAXKTCTICKETLEN;
489     if (maxLen < ticketLen)
490         return KTC_TOOBIG;
491
492     /* set return values */
493     memcpy(token->ticket, ticketP, ticketLen);
494     token->startTime = ct.BeginTimestamp;
495     token->endTime = ct.EndTimestamp;
496     if (ct.AuthHandle == -1)
497         ct.AuthHandle = 999;
498     token->kvno = ct.AuthHandle;
499 #ifndef AFS_WIN95_ENV
500     /*
501      * Session key has already been set via RPC
502      */
503 #else
504     memcpy(&token->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
505 #endif /* AFS_WIN95_ENV */
506     token->ticketLen = ticketLen;
507     if (client) {
508         strcpy(client->name, cp);
509         client->instance[0] = '\0';
510         strcpy(client->cell, cellName);
511     }
512
513     return 0;
514 }
515
516 int
517 ktc_ListTokens(int cellNum, int *cellNumP, struct ktc_principal *server)
518 {
519     struct ViceIoctl iob;
520     char tbuffer[1024];
521     char *tp, *cp;
522     int newIter, ticketLen, temp;
523     int code;
524
525     tp = tbuffer;
526
527     /* iterator */
528     memcpy(tp, &cellNum, sizeof(cellNum));
529     tp += sizeof(cellNum);
530
531     /* do pioctl */
532     iob.in = tbuffer;
533     iob.in_size = tp - tbuffer;
534     iob.out = tbuffer;
535     iob.out_size = sizeof(tbuffer);
536
537     code = pioctl(0, VIOCGETTOK, &iob, 0);
538     if (code) {
539         if (code == -1) {
540             if (errno == ESRCH)
541                 return KTC_NOCELL;
542             else if (errno == ENODEV)
543                 return KTC_NOCM;
544             else if (errno == EINVAL)
545                 return KTC_INVAL;
546             else if (errno == EDOM)
547                 return KTC_NOENT;
548             else
549                 return KTC_PIOCTLFAIL;
550         } else
551             return KTC_PIOCTLFAIL;
552     }
553
554     cp = tbuffer;
555
556     /* new iterator */
557     memcpy(&newIter, cp, sizeof(newIter));
558     cp += sizeof(newIter);
559
560     /* ticket length */
561     memcpy(&ticketLen, cp, sizeof(ticketLen));
562     cp += sizeof(ticketLen);
563
564     /* skip over ticket */
565     cp += ticketLen;
566
567     /* clear token size */
568     memcpy(&temp, cp, sizeof(temp));
569     cp += sizeof(temp);
570     if (temp != sizeof(struct ClearToken))
571         return KTC_ERROR;
572
573     /* skip over clear token */
574     cp += sizeof(struct ClearToken);
575
576     /* skip over primary flag */
577     cp += sizeof(temp);
578
579     /* cell name is here */
580
581     /* set return values */
582     strcpy(server->cell, cp);
583     server->instance[0] = '\0';
584     strcpy(server->name, "afs");
585
586     *cellNumP = newIter;
587     return 0;
588 }
589
590 int
591 ktc_ForgetToken(struct ktc_principal *server)
592 {
593     struct ViceIoctl iob;
594     char tbuffer[1024];
595     char *tp;
596     int code;
597
598     if (strcmp(server->name, "afs")) {
599         return ForgetOneLocalToken(server);
600     }
601
602     tp = tbuffer;
603
604     /* cell name */
605     strcpy(tp, server->cell);
606     tp += strlen(tp) + 1;
607
608     /* do pioctl */
609     iob.in = tbuffer;
610     iob.in_size = tp - tbuffer;
611     iob.out = tbuffer;
612     iob.out_size = sizeof(tbuffer);
613
614     code = pioctl(0, VIOCDELTOK, &iob, 0);
615     if (code) {
616         if (code == -1) {
617             if (errno == ESRCH)
618                 return KTC_NOCELL;
619             else if (errno == EDOM)
620                 return KTC_NOENT;
621             else if (errno == ENODEV)
622                 return KTC_NOCM;
623             else
624                 return KTC_PIOCTLFAIL;
625         } else
626             return KTC_PIOCTLFAIL;
627     }
628     return 0;
629 }
630
631 int
632 ktc_ForgetAllTokens()
633 {
634     struct ViceIoctl iob;
635     char tbuffer[1024];
636     int code;
637
638     (void)ForgetLocalTokens();
639
640     /* do pioctl */
641     iob.in = tbuffer;
642     iob.in_size = 0;
643     iob.out = tbuffer;
644     iob.out_size = sizeof(tbuffer);
645
646     code = pioctl(0, VIOCDELALLTOK, &iob, 0);
647     if (code) {
648         if (code == -1) {
649             if (errno == ENODEV)
650                 return KTC_NOCM;
651             else
652                 return KTC_PIOCTLFAIL;
653         } else
654             return KTC_PIOCTLFAIL;
655     }
656     return 0;
657 }
658
659 int
660 ktc_OldPioctl()
661 {
662     return 1;
663 }
664
665
666 #define MAXLOCALTOKENS 4
667
668 static struct {
669     int valid;
670     struct ktc_principal server;
671     struct ktc_principal client;
672     struct ktc_token token;
673 } local_tokens[MAXLOCALTOKENS] = {
674 0};
675
676 static int
677 SetLocalToken(struct ktc_principal *aserver, struct ktc_token *atoken,
678               struct ktc_principal *aclient, afs_int32 flags)
679 {
680     int found = -1;
681     int i;
682
683     LOCK_GLOBAL_MUTEX for (i = 0; i < MAXLOCALTOKENS; i++)
684         if (local_tokens[i].valid) {
685             if ((strcmp(local_tokens[i].server.name, aserver->name) == 0)
686                 && (strcmp(local_tokens[i].server.instance, aserver->instance)
687                     == 0)
688                 && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
689                 found = i;      /* replace existing entry */
690                 break;
691             }
692         } else if (found == -1)
693             found = i;          /* remember empty slot but keep looking for a match */
694     if (found == -1) {
695         UNLOCK_GLOBAL_MUTEX return KTC_NOENT;
696     }
697     memcpy(&local_tokens[found].token, atoken, sizeof(struct ktc_token));
698     memcpy(&local_tokens[found].server, aserver,
699            sizeof(struct ktc_principal));
700     memcpy(&local_tokens[found].client, aclient,
701            sizeof(struct ktc_principal));
702     local_tokens[found].valid = 1;
703     UNLOCK_GLOBAL_MUTEX return 0;
704 }
705
706
707 static int
708 GetLocalToken(struct ktc_principal *aserver, struct ktc_token *atoken,
709               int atokenLen, struct ktc_principal *aclient)
710 {
711     int i;
712
713     LOCK_GLOBAL_MUTEX for (i = 0; i < MAXLOCALTOKENS; i++)
714         if (local_tokens[i].valid
715             && (strcmp(local_tokens[i].server.name, aserver->name) == 0)
716             && (strcmp(local_tokens[i].server.instance, aserver->instance) ==
717                 0)
718             && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
719             memcpy(atoken, &local_tokens[i].token,
720                    min(atokenLen, sizeof(struct ktc_token)));
721             memcpy(aclient, &local_tokens[i].client,
722                    sizeof(struct ktc_principal));
723             UNLOCK_GLOBAL_MUTEX return 0;
724         }
725     UNLOCK_GLOBAL_MUTEX return KTC_NOENT;
726 }
727
728
729 static int
730 ForgetLocalTokens()
731 {
732     int i;
733
734     LOCK_GLOBAL_MUTEX for (i = 0; i < MAXLOCALTOKENS; i++) {
735         local_tokens[i].valid = 0;
736         memset(&local_tokens[i].token.sessionKey, 0,
737                sizeof(struct ktc_encryptionKey));
738     }
739     UNLOCK_GLOBAL_MUTEX return 0;
740 }
741
742
743 static int
744 ForgetOneLocalToken(struct ktc_principal *aserver)
745 {
746     int i;
747
748     LOCK_GLOBAL_MUTEX for (i = 0; i < MAXLOCALTOKENS; i++) {
749         if (local_tokens[i].valid
750             && (strcmp(local_tokens[i].server.name, aserver->name) == 0)
751             && (strcmp(local_tokens[i].server.instance, aserver->instance) ==
752                 0)
753             && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
754             local_tokens[i].valid = 0;
755             memset(&local_tokens[i].token.sessionKey, 0,
756                    sizeof(struct ktc_encryptionKey));
757             UNLOCK_GLOBAL_MUTEX return 0;
758         }
759     }
760     UNLOCK_GLOBAL_MUTEX return KTC_NOENT;
761 }