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