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