e5032bb74cfaed981982679ed607b3a17f4e273f
[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 #include <roken.h>
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 "afs/afsrpc.h"
29 #include <afs/vice.h>
30 #include "auth.h"
31 #include <afs/afsutil.h>
32 #include "token.h"
33
34 /* TBUFFERSIZE must be at least 512 larger than KTCMAXTICKETSIZE */
35 #define TBUFFERSIZE 12512
36
37 /* Forward declarations for local token cache. */
38 static int SetLocalToken(struct ktc_principal *aserver,
39                          struct ktc_token *atoken,
40                          struct ktc_principal *aclient, afs_int32 flags);
41 static int GetLocalToken(struct ktc_principal *aserver,
42                          struct ktc_token *atoken, int atokenLen,
43                          struct ktc_principal *aclient);
44 static int ForgetLocalTokens();
45 static int ForgetOneLocalToken(struct ktc_principal *aserver);
46
47
48 static char AFSConfigKeyName[] =
49     "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters";
50
51 static char AFSGlobalKTCMutexName[] = "Global\\AFS_KTC_Mutex";
52 static char AFSKTCMutexName[] = "AFS_KTC_Mutex";
53
54 #define MAXPIOCTLTOKENLEN \
55 (3*sizeof(afs_int32)+MAXKTCTICKETLEN+sizeof(struct ClearToken)+MAXKTCREALMLEN)
56
57 /*
58  * Support for RPC's to send and receive session keys
59  *
60  * Session keys must not be sent and received in the clear.  We have no
61  * way to piggyback encryption on SMB, so we use a separate RPC, using
62  * packet privacy (when available).  In SetToken, the RPC is done first;
63  * in GetToken, the pioctl is done first.
64  */
65
66 char rpcErr[256];
67
68 void __RPC_FAR *__RPC_USER
69 midl_user_allocate(size_t cBytes)
70 {
71     return ((void __RPC_FAR *)malloc(cBytes));
72 }
73
74 void __RPC_USER
75 midl_user_free(void __RPC_FAR * p)
76 {
77     free(p);
78 }
79
80 /*
81  * Determine the server name to be used in the RPC binding.  If it is
82  * the same as the client (i.e. standalone, non-gateway), NULL can be
83  * used, so it is not necessary to call gethostbyname().
84  */
85 void
86 getservername(char **snp, unsigned int snSize)
87 {
88     HKEY parmKey;
89     long code;
90
91     code =
92         RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE,
93                      &parmKey);
94     if (code != ERROR_SUCCESS)
95         goto nogateway;
96     code = RegQueryValueEx(parmKey, "Gateway", NULL, NULL, *snp, &snSize);
97     RegCloseKey(parmKey);
98     if (code == ERROR_SUCCESS)
99         return;
100   nogateway:
101     /* No gateway name in registry; use ourself */
102     *snp = NULL;
103 }
104
105 RPC_STATUS
106 send_key(afs_uuid_t uuid, char sessionKey[8])
107 {
108     RPC_STATUS status;
109     char *stringBinding = NULL;
110     ULONG authnLevel, authnSvc;
111     char serverName[256];
112     char *serverNamep = serverName;
113     char encrypt[32];
114     BOOL encryptionOff = FALSE;
115     char protseq[32];
116
117     /* Encryption on by default */
118     if (GetEnvironmentVariable("AFS_RPC_ENCRYPT", encrypt, sizeof(encrypt)))
119         if (!_stricmp(encrypt, "OFF"))
120             encryptionOff = TRUE;
121
122     /* Protocol sequence is local by default */
123     if (!GetEnvironmentVariable("AFS_RPC_PROTSEQ", protseq, sizeof(protseq)))
124         strcpy(protseq, "ncalrpc");
125
126     /* Server name */
127     getservername(&serverNamep, sizeof(serverName));
128
129     status = RpcStringBindingCompose("",        /* obj uuid */
130                                      protseq, serverNamep, "",  /* endpoint */
131                                      "",        /* protocol options */
132                                      &stringBinding);
133     if (status != RPC_S_OK)
134         goto cleanup;
135
136     status = RpcBindingFromStringBinding(stringBinding, &hAfsHandle);
137     if (status != RPC_S_OK)
138         goto cleanup;
139
140     /*
141      * On Windows 95/98, we must resolve the binding before calling
142      * SetAuthInfo.  On Windows NT, we don't have to resolve yet,
143      * but it does no harm.
144      */
145     status = RpcEpResolveBinding(hAfsHandle, afsrpc_v1_0_c_ifspec);
146     if (status != RPC_S_OK)
147         goto cleanup;
148
149     if (encryptionOff) {
150         authnLevel = RPC_C_AUTHN_LEVEL_NONE;
151         authnSvc = RPC_C_AUTHN_WINNT;
152     } else {
153         authnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
154         authnSvc = RPC_C_AUTHN_WINNT;
155     }
156
157     status =
158         RpcBindingSetAuthInfo(hAfsHandle, NULL, authnLevel, authnSvc, NULL,
159                               RPC_C_AUTHZ_NONE);
160     if (status != RPC_S_OK)
161         goto cleanup;
162
163     RpcTryExcept {
164         status = AFSRPC_SetToken(uuid, sessionKey);
165     }
166     RpcExcept(1) {
167         status = RpcExceptionCode();
168     }
169     RpcEndExcept cleanup:if (stringBinding)
170           RpcStringFree(&stringBinding);
171
172     if (hAfsHandle != NULL)
173         RpcBindingFree(&hAfsHandle);
174
175     return status;
176 }
177
178 RPC_STATUS
179 receive_key(afs_uuid_t uuid, char sessionKey[8])
180 {
181     RPC_STATUS status;
182     char *stringBinding = NULL;
183     ULONG authnLevel, authnSvc;
184     char serverName[256];
185     char *serverNamep = serverName;
186     char encrypt[32];
187     BOOL encryptionOff = FALSE;
188     char protseq[32];
189
190     /* Encryption on by default */
191     if (GetEnvironmentVariable("AFS_RPC_ENCRYPT", encrypt, sizeof(encrypt)))
192         if (!_stricmp(encrypt, "OFF"))
193             encryptionOff = TRUE;
194
195     /* Protocol sequence is local by default */
196     if (!GetEnvironmentVariable("AFS_RPC_PROTSEQ", protseq, sizeof(protseq)))
197         strcpy(protseq, "ncalrpc");
198
199     /* Server name */
200     getservername(&serverNamep, sizeof(serverName));
201
202     status = RpcStringBindingCompose("",        /* obj uuid */
203                                      protseq, serverNamep, "",  /* endpoint */
204                                      "",        /* protocol options */
205                                      &stringBinding);
206     if (status != RPC_S_OK)
207         goto cleanup;
208
209     status = RpcBindingFromStringBinding(stringBinding, &hAfsHandle);
210     if (status != RPC_S_OK)
211         goto cleanup;
212
213     /*
214      * On Windows 95/98, we must resolve the binding before calling
215      * SetAuthInfo.  On Windows NT, we don't have to resolve yet,
216      * but it does no harm.
217      */
218     status = RpcEpResolveBinding(hAfsHandle, afsrpc_v1_0_c_ifspec);
219     if (status != RPC_S_OK)
220         goto cleanup;
221
222     if (encryptionOff) {
223         authnLevel = RPC_C_AUTHN_LEVEL_NONE;
224         authnSvc = RPC_C_AUTHN_WINNT;
225     } else {
226         authnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
227         authnSvc = RPC_C_AUTHN_WINNT;
228     }
229
230     status =
231         RpcBindingSetAuthInfo(hAfsHandle, NULL, authnLevel, authnSvc, NULL,
232                               RPC_C_AUTHZ_NONE);
233     if (status != RPC_S_OK)
234         goto cleanup;
235
236     RpcTryExcept {
237         status = AFSRPC_GetToken(uuid, sessionKey);
238     }
239     RpcExcept(1) {
240         status = RpcExceptionCode();
241     }
242     RpcEndExcept cleanup:if (stringBinding)
243           RpcStringFree(&stringBinding);
244
245     if (hAfsHandle != NULL)
246         RpcBindingFree(&hAfsHandle);
247
248     return status;
249 }
250
251 int
252 ktc_SetToken(struct ktc_principal *server, struct ktc_token *token,
253              struct ktc_principal *client, int flags)
254 {
255     struct ViceIoctl iob;
256     char tbuffer[TBUFFERSIZE];
257     int len;
258     char *tp;
259     struct ClearToken ct;
260     int temp;
261     int code;
262     RPC_STATUS status;
263     afs_uuid_t uuid;
264     HANDLE ktcMutex = NULL;
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     len = sizeof(token->ticketLen);
280
281     /* ticket */
282     if (len + token->ticketLen > TBUFFERSIZE)
283         return KTC_INVAL;
284     memcpy(tp, token->ticket, token->ticketLen);
285     tp += token->ticketLen;
286     len += token->ticketLen;
287
288     /* clear token */
289     ct.AuthHandle = token->kvno;
290     /*
291      * Instead of sending the session key in the clear, we zero it,
292      * and send it later, via RPC, encrypted.
293      */
294 #ifndef AFS_WIN95_ENV
295     /*
296      * memcpy(ct.HandShakeKey, &token->sessionKey, sizeof(token->sessionKey));
297      */
298     memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
299 #else
300     memcpy(ct.HandShakeKey, &token->sessionKey, sizeof(token->sessionKey));
301 #endif
302     ct.BeginTimestamp = token->startTime;
303     ct.EndTimestamp = token->endTime;
304     if (ct.BeginTimestamp == 0)
305         ct.BeginTimestamp = 1;
306
307     /* We don't know from Vice ID's yet */
308     ct.ViceId = 37;             /* XXX */
309     if (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1)
310         ct.BeginTimestamp++;    /* force lifetime to be even */
311
312     /* size of clear token */
313     if (len + sizeof(temp) > TBUFFERSIZE)
314         return KTC_INVAL;
315     temp = sizeof(struct ClearToken);
316     memcpy(tp, &temp, sizeof(temp));
317     tp += sizeof(temp);
318     len += sizeof(temp);
319
320     /* clear token itself */
321     if (len + sizeof(ct) > TBUFFERSIZE)
322         return KTC_INVAL;
323     memcpy(tp, &ct, sizeof(ct));
324     tp += sizeof(ct);
325     len += sizeof(ct);
326
327     /* flags; on NT there is no setpag flag, but there is an
328      * integrated logon flag */
329     if (len + sizeof(temp) > TBUFFERSIZE)
330         return KTC_INVAL;
331     temp = ((flags & AFS_SETTOK_LOGON) ? PIOCTL_LOGON : 0);
332     memcpy(tp, &temp, sizeof(temp));
333     tp += sizeof(temp);
334     len += sizeof(temp);
335
336     /* cell name */
337     temp = (int)strlen(server->cell) + 1;
338     if (len + temp > TBUFFERSIZE ||
339         temp > MAXKTCREALMLEN)
340         return KTC_INVAL;
341     strcpy(tp, server->cell);
342     tp += temp;
343     len += temp;
344
345     /* user name */
346     temp = (int)strlen(client->name) + 1;
347     if (len + temp > TBUFFERSIZE ||
348         temp > MAXKTCNAMELEN)
349         return KTC_INVAL;
350     strcpy(tp, client->name);
351     tp += temp;
352     len += temp;
353
354     /* we need the SMB user name to associate the tokens with in the
355      * integrated logon case. */
356     if (flags & AFS_SETTOK_LOGON) {
357         if (client->smbname == NULL)
358             temp = 1;
359         else
360             temp = (int)strlen(client->smbname) + 1;
361         if (temp == 1 ||
362             len + temp > TBUFFERSIZE ||
363             temp > MAXKTCNAMELEN)
364             return KTC_INVAL;
365         strcpy(tp, client->smbname);
366         tp += temp;
367         len += temp;
368     }
369
370     /* uuid */
371     if (len + sizeof(uuid) > TBUFFERSIZE)
372         return KTC_INVAL;
373     status = UuidCreate((UUID *) & uuid);
374     memcpy(tp, &uuid, sizeof(uuid));
375     tp += sizeof(uuid);
376     len += sizeof(uuid);
377
378 #ifndef AFS_WIN95_ENV
379     ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
380     if (ktcMutex == NULL)
381         return KTC_PIOCTLFAIL;
382     if (GetLastError() == ERROR_ALREADY_EXISTS) {
383         if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
384             CloseHandle(ktcMutex);
385             return KTC_PIOCTLFAIL;
386         }
387     }
388
389     /* RPC to send session key */
390     status = send_key(uuid, token->sessionKey.data);
391     if (status != RPC_S_OK) {
392         if (status == 1)
393             strcpy(rpcErr, "RPC failure in AFS gateway");
394         else
395             DceErrorInqText(status, rpcErr);
396         if (status == RPC_S_SERVER_UNAVAILABLE ||
397             status == EPT_S_NOT_REGISTERED) {
398             ReleaseMutex(ktcMutex);
399             CloseHandle(ktcMutex);
400             return KTC_NOCMRPC;
401         } else {
402             ReleaseMutex(ktcMutex);
403             CloseHandle(ktcMutex);
404             return KTC_RPC;
405         }
406     }
407 #endif /* AFS_WIN95_ENV */
408
409     /* set up for pioctl */
410     iob.in = tbuffer;
411     iob.in_size = (long)(tp - tbuffer);
412     iob.out = tbuffer;
413     iob.out_size = sizeof(tbuffer);
414
415     code = pioctl(0, VIOCSETTOK, &iob, 0);
416
417 #ifndef AFS_WIN95_ENV
418     ReleaseMutex(ktcMutex);
419     CloseHandle(ktcMutex);
420 #endif /* AFS_WIN95_ENV */
421
422     if (code) {
423         if (code == -1) {
424             if (errno == ESRCH)
425                 return KTC_NOCELL;
426             else if (errno == ENODEV)
427                 return KTC_NOCM;
428             else if (errno == EINVAL)
429                 return KTC_INVAL;
430             else
431                 return KTC_PIOCTLFAIL;
432         } else
433             return KTC_PIOCTLFAIL;
434     }
435
436     return 0;
437 }
438
439 int
440 ktc_SetTokenEx(struct ktc_setTokenData *token)
441 {
442     /* Not yet implemented */
443     return KTC_PIOCTLFAIL;
444 }
445
446 int
447 ktc_GetToken(struct ktc_principal *server, struct ktc_token *token,
448              int tokenLen, struct ktc_principal *client)
449 {
450     struct ViceIoctl iob;
451     char tbuffer[TBUFFERSIZE];
452     size_t len;
453     char *tp, *cp;
454     char *ticketP;
455     int ticketLen, temp;
456     struct ClearToken ct;
457     char *cellName;
458     int cellNameSize;
459     int maxLen;
460     int code;
461     RPC_STATUS status;
462     afs_uuid_t uuid;
463     HANDLE ktcMutex = NULL;
464
465     tp = tbuffer;
466
467     /* check to see if the user is requesting tokens for a principal
468      * other than afs.  If so, check the local token cache.
469      */
470     if (strcmp(server->name, "afs")) {
471         return GetLocalToken(server, token, tokenLen, client);
472     }
473
474     /* cell name */
475     len = strlen(server->cell) + 1;
476     strcpy(tp, server->cell);
477     tp += len;
478
479     /* uuid */
480     status = UuidCreate((UUID *) & uuid);
481     memcpy(tp, &uuid, sizeof(uuid));
482     tp += sizeof(uuid);
483     len += sizeof(uuid);
484
485     iob.in = tbuffer;
486     iob.in_size = (long)(tp - tbuffer);
487     iob.out = tbuffer;
488     iob.out_size = sizeof(tbuffer);
489
490 #ifndef AFS_WIN95_ENV
491     ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
492     if (ktcMutex == NULL)
493         return KTC_PIOCTLFAIL;
494     if (GetLastError() == ERROR_ALREADY_EXISTS) {
495         if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
496             CloseHandle(ktcMutex);
497             return KTC_PIOCTLFAIL;
498         }
499     }
500 #endif /* AFS_WIN95_ENV */
501
502     code = pioctl(0, VIOCNEWGETTOK, &iob, 0);
503     if (code) {
504 #ifndef AFS_WIN95_ENV
505         ReleaseMutex(ktcMutex);
506         CloseHandle(ktcMutex);
507 #endif /* AFS_WIN95_ENV */
508         if (code == -1) {
509             if (errno == ESRCH)
510                 return KTC_NOCELL;
511             else if (errno == ENODEV)
512                 return KTC_NOCM;
513             else if (errno == EINVAL)
514                 return KTC_INVAL;
515             else if (errno == EDOM)
516                 return KTC_NOENT;
517             else
518                 return KTC_PIOCTLFAIL;
519         } else
520             return KTC_PIOCTLFAIL;
521     }
522 #ifndef AFS_WIN95_ENV
523     /* get rid of RPC for win95 build */
524     /* RPC to receive session key */
525     status = receive_key(uuid, token->sessionKey.data);
526
527     ReleaseMutex(ktcMutex);
528     CloseHandle(ktcMutex);
529
530     if (status != RPC_S_OK) {
531         if (status == 1)
532             strcpy(rpcErr, "RPC failure in AFS gateway");
533         else
534             DceErrorInqText(status, rpcErr);
535         if (status == RPC_S_SERVER_UNAVAILABLE
536             || status == EPT_S_NOT_REGISTERED)
537             return KTC_NOCMRPC;
538         else
539             return KTC_RPC;
540     }
541 #endif /* AFS_WIN95_ENV */
542
543     cp = tbuffer;
544
545     /* ticket length */
546     memcpy(&ticketLen, cp, sizeof(ticketLen));
547     cp += sizeof(ticketLen);
548     len = sizeof(ticketLen);
549
550     /* remember where ticket is and skip over it */
551     if (len + ticketLen > TBUFFERSIZE ||
552         len + ticketLen > iob.out_size)
553         return KTC_ERROR;
554     ticketP = cp;
555     cp += ticketLen;
556     len += ticketLen;
557
558     /* size of clear token */
559     if (len + sizeof(temp) > TBUFFERSIZE ||
560         len + sizeof(temp) > iob.out_size)
561         return KTC_ERROR;
562     memcpy(&temp, cp, sizeof(temp));
563     cp += sizeof(temp);
564     len += sizeof(temp);
565     if (temp != sizeof(ct))
566         return KTC_ERROR;
567
568     /* clear token */
569     if (len + temp > TBUFFERSIZE ||
570         len + temp > iob.out_size)
571         return KTC_ERROR;
572     memcpy(&ct, cp, temp);
573     cp += temp;
574     len += temp;
575
576     /* skip over primary flag */
577     if (len + sizeof(temp) > TBUFFERSIZE ||
578         len + sizeof(temp) > iob.out_size)
579         return KTC_ERROR;
580     cp += sizeof(temp);
581     len += sizeof(temp);
582
583     /* remember cell name and skip over it */
584     cellName = cp;
585     cellNameSize = (int)strlen(cp);
586     if (len + cellNameSize + 1 > TBUFFERSIZE ||
587         len + cellNameSize + 1 > iob.out_size)
588         return KTC_ERROR;
589     cp += cellNameSize + 1;
590     len += cellNameSize + 1;
591
592     /* user name is here */
593
594     /* check that ticket will fit
595      * this compares the size of the ktc_token allocated by the app
596      * which might be smaller than the current definition of MAXKTCTICKETLEN
597      */
598     maxLen = tokenLen - sizeof(struct ktc_token) + MAXKTCTICKETLEN;
599     if (maxLen < ticketLen)
600         return KTC_TOOBIG;
601
602     /* set return values */
603     memcpy(token->ticket, ticketP, ticketLen);
604     token->startTime = ct.BeginTimestamp;
605     token->endTime = ct.EndTimestamp;
606     if (ct.AuthHandle == -1)
607         ct.AuthHandle = 999;
608     token->kvno = ct.AuthHandle;
609 #ifndef AFS_WIN95_ENV
610     /*
611      * Session key has already been set via RPC
612      */
613 #else
614     memcpy(&token->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
615 #endif /* AFS_WIN95_ENV */
616     token->ticketLen = ticketLen;
617     if (client) {
618         strcpy(client->name, cp);
619         client->instance[0] = '\0';
620         strcpy(client->cell, cellName);
621     }
622
623     return 0;
624 }
625
626 /*!
627  * Get a token, given the cell that we need to get information for
628  *
629  * @param cellName
630  *      The name of the cell we're getting the token for - if NULL, we'll
631  *      get information for the primary cell
632  */
633 int
634 ktc_GetTokenEx(char *cellName, struct ktc_setTokenData **tokenSet) {
635     struct ViceIoctl iob;
636     char tbuffer[MAXPIOCTLTOKENLEN];
637     char *tp;
638     afs_int32 code;
639     XDR xdrs;
640     HANDLE ktcMutex = NULL;
641
642     tp = tbuffer;
643
644     /* If we have a cellName, write it out here */
645     if (cellName) {
646         memcpy(tp, cellName, strlen(cellName) +1);
647         tp += strlen(cellName)+1;
648     }
649
650     iob.in = tbuffer;
651     iob.in_size = tp - tbuffer;
652     iob.out = tbuffer;
653     iob.out_size = sizeof(tbuffer);
654
655 #ifndef AFS_WIN95_ENV
656     ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
657     if (ktcMutex == NULL)
658         return KTC_PIOCTLFAIL;
659     if (GetLastError() == ERROR_ALREADY_EXISTS) {
660         if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
661             CloseHandle(ktcMutex);
662             return KTC_PIOCTLFAIL;
663         }
664     }
665 #endif /* AFS_WIN95_ENV */
666
667 #if 0
668     code = pioctl(0, VIOC_GETTOK2, &iob, 0);
669 #else
670     code = -1;   /* not yet implemented */
671         errno = EINVAL;
672 #endif
673
674 #ifndef AFS_WIN95_ENV
675     ReleaseMutex(ktcMutex);
676     CloseHandle(ktcMutex);
677 #endif /* AFS_WIN95_ENV */
678
679     /* If we can't use the new pioctl, the fall back to the old one. We then
680      * need to convert the rxkad token we get back into the new format
681      */
682     if (code == -1 && errno == EINVAL) {
683         struct ktc_principal server;
684         struct ktc_principal client;
685         struct ktc_tokenUnion token;
686         struct ktc_token *ktcToken; /* too huge for the stack */
687
688         memset(&server, 0, sizeof(server));
689         ktcToken = malloc(sizeof(struct ktc_token));
690         if (ktcToken == NULL)
691             return ENOMEM;
692         memset(ktcToken, 0, sizeof(struct ktc_token));
693
694         strcpy(server.name, "afs");
695         strcpy(server.cell, cellName);
696         code = ktc_GetToken(&server, ktcToken, sizeof(struct ktc_token),
697                             &client);
698         if (code == 0) {
699             *tokenSet = token_buildTokenJar(cellName);
700             token.at_type = AFSTOKEN_UNION_KAD;
701             token.ktc_tokenUnion_u.at_kad.rk_kvno = ktcToken->kvno;
702             memcpy(token.ktc_tokenUnion_u.at_kad.rk_key,
703                    ktcToken->sessionKey.data, 8);
704
705             token.ktc_tokenUnion_u.at_kad.rk_begintime = ktcToken->startTime;
706             token.ktc_tokenUnion_u.at_kad.rk_endtime   = ktcToken->endTime;
707             token.ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len
708                 = ktcToken->ticketLen;
709             token.ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val
710                 = ktcToken->ticket;
711
712             token_addToken(*tokenSet, &token);
713
714             memset(ktcToken, 0, sizeof(struct ktc_token));
715         }
716         free(ktcToken);
717         return code;
718     }
719     if (code)
720         return KTC_PIOCTLFAIL;
721
722     *tokenSet = malloc(sizeof(struct ktc_setTokenData));
723     if (*tokenSet == NULL)
724         return ENOMEM;
725     memset(*tokenSet, 0, sizeof(struct ktc_setTokenData));
726
727     xdrmem_create(&xdrs, iob.out, iob.out_size, XDR_DECODE);
728     if (!xdr_ktc_setTokenData(&xdrs, *tokenSet)) {
729         free(*tokenSet);
730         *tokenSet = NULL;
731         xdr_destroy(&xdrs);
732         return EINVAL;
733     }
734     xdr_destroy(&xdrs);
735     return 0;
736 }
737
738 int
739 ktc_ListTokens(int cellNum, int *cellNumP, struct ktc_principal *server)
740 {
741     struct ViceIoctl iob;
742     char tbuffer[TBUFFERSIZE];
743     int len;
744     char *tp, *cp;
745     int newIter, ticketLen, temp;
746     int code;
747     HANDLE ktcMutex = NULL;
748
749 #ifndef AFS_WIN95_ENV
750     ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
751     if (ktcMutex == NULL)
752         return KTC_PIOCTLFAIL;
753     if (GetLastError() == ERROR_ALREADY_EXISTS) {
754         if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
755             CloseHandle(ktcMutex);
756             return KTC_PIOCTLFAIL;
757         }
758     }
759 #endif /* AFS_WIN95_ENV */
760
761     tp = tbuffer;
762
763     /* iterator */
764     memcpy(tp, &cellNum, sizeof(cellNum));
765     tp += sizeof(cellNum);
766
767     /* do pioctl */
768     iob.in = tbuffer;
769     iob.in_size = (long)(tp - tbuffer);
770     iob.out = tbuffer;
771     iob.out_size = sizeof(tbuffer);
772
773     code = pioctl(0, VIOCGETTOK, &iob, 0);
774
775 #ifndef AFS_WIN95_ENV
776     ReleaseMutex(ktcMutex);
777     CloseHandle(ktcMutex);
778 #endif /* AFS_WIN95_ENV */
779
780     if (code) {
781         if (code == -1) {
782             if (errno == ESRCH)
783                 return KTC_NOCELL;
784             else if (errno == ENODEV)
785                 return KTC_NOCM;
786             else if (errno == EINVAL)
787                 return KTC_INVAL;
788             else if (errno == EDOM)
789                 return KTC_NOENT;
790             else
791                 return KTC_PIOCTLFAIL;
792         } else
793             return KTC_PIOCTLFAIL;
794     }
795
796     cp = tbuffer;
797
798     /* new iterator */
799     memcpy(&newIter, cp, sizeof(newIter));
800     cp += sizeof(newIter);
801     len = sizeof(newIter);
802
803     /* ticket length */
804     if (len + sizeof(ticketLen) > TBUFFERSIZE ||
805         len + sizeof(ticketLen) > iob.out_size)
806         return KTC_ERROR;
807     memcpy(&ticketLen, cp, sizeof(ticketLen));
808     cp += sizeof(ticketLen);
809     len += sizeof(ticketLen);
810
811     /* skip over ticket */
812     cp += ticketLen;
813     len += ticketLen;
814
815     /* clear token size */
816     if (len + sizeof(temp) > TBUFFERSIZE ||
817         len + sizeof(temp) > iob.out_size)
818         return KTC_ERROR;
819     memcpy(&temp, cp, sizeof(temp));
820     cp += sizeof(temp);
821     len += sizeof(temp);
822     if (temp != sizeof(struct ClearToken))
823         return KTC_ERROR;
824
825     /* skip over clear token */
826     cp += sizeof(struct ClearToken);
827     len += sizeof(struct ClearToken);
828
829     /* skip over primary flag */
830     cp += sizeof(temp);
831     len += sizeof(temp);
832     if (len > TBUFFERSIZE ||
833         len > iob.out_size)
834         return KTC_ERROR;
835
836     /* cell name is here */
837
838     /* set return values */
839     if (len + temp > TBUFFERSIZE ||
840         temp > MAXKTCREALMLEN)
841         return KTC_ERROR;
842     strcpy(server->cell, cp);
843     server->instance[0] = '\0';
844     strcpy(server->name, "afs");
845
846     *cellNumP = newIter;
847     return 0;
848 }
849
850 int
851 ktc_ForgetToken(struct ktc_principal *server)
852 {
853     struct ViceIoctl iob;
854     char tbuffer[TBUFFERSIZE];
855     char *tp;
856     int code;
857     HANDLE ktcMutex = NULL;
858
859     if (strcmp(server->name, "afs")) {
860         return ForgetOneLocalToken(server);
861     }
862 #ifndef AFS_WIN95_ENV
863     ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
864     if (ktcMutex == NULL)
865         return KTC_PIOCTLFAIL;
866     if (GetLastError() == ERROR_ALREADY_EXISTS) {
867         if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
868             CloseHandle(ktcMutex);
869             return KTC_PIOCTLFAIL;
870         }
871     }
872 #endif /* AFS_WIN95_ENV */
873
874     tp = tbuffer;
875
876     /* cell name */
877     strcpy(tp, server->cell);
878     tp += strlen(tp) + 1;
879
880     /* do pioctl */
881     iob.in = tbuffer;
882     iob.in_size = (long)(tp - tbuffer);
883     iob.out = tbuffer;
884     iob.out_size = sizeof(tbuffer);
885
886     code = pioctl(0, VIOCDELTOK, &iob, 0);
887 #ifndef AFS_WIN95_ENV
888     ReleaseMutex(ktcMutex);
889     CloseHandle(ktcMutex);
890 #endif /* AFS_WIN95_ENV */
891     if (code) {
892         if (code == -1) {
893             if (errno == ESRCH)
894                 return KTC_NOCELL;
895             else if (errno == EDOM)
896                 return KTC_NOENT;
897             else if (errno == ENODEV)
898                 return KTC_NOCM;
899             else
900                 return KTC_PIOCTLFAIL;
901         } else
902             return KTC_PIOCTLFAIL;
903     }
904     return 0;
905 }
906
907 int
908 ktc_ForgetAllTokens()
909 {
910     struct ViceIoctl iob;
911     char tbuffer[TBUFFERSIZE];
912     int code;
913     HANDLE ktcMutex = NULL;
914
915     (void)ForgetLocalTokens();
916
917 #ifndef AFS_WIN95_ENV
918     ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
919     if (ktcMutex == NULL)
920         return KTC_PIOCTLFAIL;
921     if (GetLastError() == ERROR_ALREADY_EXISTS) {
922         if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
923             CloseHandle(ktcMutex);
924             return KTC_PIOCTLFAIL;
925         }
926     }
927 #endif /* AFS_WIN95_ENV */
928
929     /* do pioctl */
930     iob.in = tbuffer;
931     iob.in_size = 0;
932     iob.out = tbuffer;
933     iob.out_size = sizeof(tbuffer);
934
935     code = pioctl(0, VIOCDELALLTOK, &iob, 0);
936 #ifndef AFS_WIN95_ENV
937     ReleaseMutex(ktcMutex);
938     CloseHandle(ktcMutex);
939 #endif /* AFS_WIN95_ENV */
940     if (code) {
941         if (code == -1) {
942             if (errno == ENODEV)
943                 return KTC_NOCM;
944             else
945                 return KTC_PIOCTLFAIL;
946         } else
947             return KTC_PIOCTLFAIL;
948     }
949     return 0;
950 }
951
952 int
953 ktc_OldPioctl()
954 {
955     return 1;
956 }
957
958
959 #define MAXLOCALTOKENS 4
960
961 static struct {
962     int valid;
963     struct ktc_principal server;
964     struct ktc_principal client;
965     struct ktc_token token;
966 } local_tokens[MAXLOCALTOKENS] = {
967 0};
968
969 static int
970 SetLocalToken(struct ktc_principal *aserver, struct ktc_token *atoken,
971               struct ktc_principal *aclient, afs_int32 flags)
972 {
973     int found = -1;
974     int i;
975
976     LOCK_GLOBAL_MUTEX;
977     for (i = 0; i < MAXLOCALTOKENS; i++)
978         if (local_tokens[i].valid) {
979             if ((strcmp(local_tokens[i].server.name, aserver->name) == 0)
980                 && (strcmp(local_tokens[i].server.instance, aserver->instance)
981                     == 0)
982                 && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
983                 found = i;      /* replace existing entry */
984                 break;
985             }
986         } else if (found == -1)
987             found = i;          /* remember empty slot but keep looking for a match */
988     if (found == -1) {
989         UNLOCK_GLOBAL_MUTEX;
990         return KTC_NOENT;
991     }
992     memcpy(&local_tokens[found].token, atoken, sizeof(struct ktc_token));
993     memcpy(&local_tokens[found].server, aserver,
994            sizeof(struct ktc_principal));
995     memcpy(&local_tokens[found].client, aclient,
996            sizeof(struct ktc_principal));
997     local_tokens[found].valid = 1;
998     UNLOCK_GLOBAL_MUTEX;
999     return 0;
1000 }
1001
1002
1003 static int
1004 GetLocalToken(struct ktc_principal *aserver, struct ktc_token *atoken,
1005               int atokenLen, struct ktc_principal *aclient)
1006 {
1007     int i;
1008
1009     LOCK_GLOBAL_MUTEX;
1010     for (i = 0; i < MAXLOCALTOKENS; i++)
1011         if (local_tokens[i].valid
1012             && (strcmp(local_tokens[i].server.name, aserver->name) == 0)
1013             && (strcmp(local_tokens[i].server.instance, aserver->instance) ==
1014                 0)
1015             && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
1016             memcpy(atoken, &local_tokens[i].token,
1017                    min(atokenLen, sizeof(struct ktc_token)));
1018             memcpy(aclient, &local_tokens[i].client,
1019                    sizeof(struct ktc_principal));
1020             UNLOCK_GLOBAL_MUTEX;
1021             return 0;
1022         }
1023     UNLOCK_GLOBAL_MUTEX;
1024     return KTC_NOENT;
1025 }
1026
1027
1028 static int
1029 ForgetLocalTokens()
1030 {
1031     int i;
1032
1033     LOCK_GLOBAL_MUTEX;
1034     for (i = 0; i < MAXLOCALTOKENS; i++) {
1035         local_tokens[i].valid = 0;
1036         memset(&local_tokens[i].token.sessionKey, 0,
1037                sizeof(struct ktc_encryptionKey));
1038     }
1039     UNLOCK_GLOBAL_MUTEX;
1040     return 0;
1041 }
1042
1043
1044 static int
1045 ForgetOneLocalToken(struct ktc_principal *aserver)
1046 {
1047     int i;
1048
1049     LOCK_GLOBAL_MUTEX;
1050     for (i = 0; i < MAXLOCALTOKENS; i++) {
1051         if (local_tokens[i].valid
1052             && (strcmp(local_tokens[i].server.name, aserver->name) == 0)
1053             && (strcmp(local_tokens[i].server.instance, aserver->instance) ==
1054                 0)
1055             && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
1056             local_tokens[i].valid = 0;
1057             memset(&local_tokens[i].token.sessionKey, 0,
1058                    sizeof(struct ktc_encryptionKey));
1059             UNLOCK_GLOBAL_MUTEX;
1060             return 0;
1061         }
1062     }
1063     UNLOCK_GLOBAL_MUTEX;
1064     return KTC_NOENT;
1065 }
1066
1067 /*!
1068  * An iterator which can list all cells with tokens in the cache
1069  *
1070  * This function may be used to list the names of all cells for which
1071  * tokens exist in the current cache. The first time that it is called,
1072  * prevIndex should be set to 0. On all subsequent calls, prevIndex
1073  * should be set to the value returned in newIndex by the last call
1074  * to the function. Note that there is no guarantee that the index value
1075  * is monotonically increasing.
1076  *
1077  * @param prevIndex
1078  *      The index returned by the last call, or 0 if this is the first
1079  *      call in an iteration
1080  * @param newIndex
1081  *      A pointer to an int which, upon return, will hold the next value
1082  *      to be used.
1083  * @param cellName
1084  *      A pointer to a char * which, upon return, will hold a cellname.
1085  *      This must be freed by the caller using free()
1086  */
1087
1088 int
1089 ktc_ListTokensEx(int prevIndex, int *newIndex, char **cellName) {
1090     struct ViceIoctl iob;
1091     char tbuffer[MAXPIOCTLTOKENLEN];
1092     afs_int32 code;
1093     afs_int32 index;
1094     struct ktc_setTokenData tokenSet;
1095     XDR xdrs;
1096     HANDLE ktcMutex = NULL;
1097
1098     memset(&tokenSet, 0, sizeof(tokenSet));
1099
1100     *cellName = NULL;
1101     *newIndex = prevIndex;
1102
1103     index = prevIndex;
1104
1105     while (index<100) { /* Safety, incase of pioctl failure */
1106         memset(tbuffer, 0, sizeof(tbuffer));
1107         iob.in = tbuffer;
1108         memcpy(tbuffer, &index, sizeof(afs_int32));
1109         iob.in_size = sizeof(afs_int32);
1110         iob.out = tbuffer;
1111         iob.out_size = sizeof(tbuffer);
1112
1113 #if 0
1114         code = pioctl(0, VIOC_GETTOK2, &iob, 0);
1115 #else
1116     code = -1;      /* not yet implemented */
1117         errno = EINVAL;
1118 #endif
1119
1120         /* Can't use new pioctl, so must use old one */
1121         if (code == -1 && errno == EINVAL) {
1122             struct ktc_principal server;
1123
1124             code = ktc_ListTokens(index, newIndex, &server);
1125             if (code == 0)
1126                 *cellName = strdup(server.cell);
1127             return code;
1128         }
1129
1130         if (code == 0) {
1131             /* Got a token from the pioctl. Now we throw it away,
1132              * so we can return just a cellname. This is rather wasteful,
1133              * but it's what the old API does. Ho hum.  */
1134
1135             xdrmem_create(&xdrs, iob.out, iob.out_size, XDR_DECODE);
1136             if (!xdr_ktc_setTokenData(&xdrs, &tokenSet)) {
1137                 xdr_destroy(&xdrs);
1138                 return EINVAL;
1139             }
1140             xdr_destroy(&xdrs);
1141             *cellName = strdup(tokenSet.cell);
1142             xdr_free((xdrproc_t)xdr_ktc_setTokenData, &tokenSet);
1143             *newIndex = index + 1;
1144             return 0;
1145         }
1146         index++;
1147     }
1148     return KTC_PIOCTLFAIL;
1149 }
1150