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