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