2 * Copyright 2000, International Business Machines Corporation and others.
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
10 /* ticket caching code */
12 #include <afsconfig.h>
13 #include <afs/param.h>
18 #include <afs/pthread_glock.h>
19 #include <sys/types.h>
25 #include <afs/smb_iocons.h>
26 #include <afs/pioctl_nt.h>
27 #include "afs/afsrpc.h"
30 #include <afs/afsutil.h>
33 /* TBUFFERSIZE must be at least 512 larger than KTCMAXTICKETSIZE */
34 #define TBUFFERSIZE 12512
36 /* Forward declarations for local token cache. */
37 static int SetLocalToken(struct ktc_principal *aserver,
38 struct ktc_token *atoken,
39 struct ktc_principal *aclient, afs_int32 flags);
40 static int GetLocalToken(struct ktc_principal *aserver,
41 struct ktc_token *atoken, int atokenLen,
42 struct ktc_principal *aclient);
43 static int ForgetLocalTokens();
44 static int ForgetOneLocalToken(struct ktc_principal *aserver);
47 static char AFSConfigKeyName[] =
48 "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters";
50 static char AFSGlobalKTCMutexName[] = "Global\\AFS_KTC_Mutex";
51 static char AFSKTCMutexName[] = "AFS_KTC_Mutex";
54 * Support for RPC's to send and receive session keys
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.
64 void __RPC_FAR *__RPC_USER
65 midl_user_allocate(size_t cBytes)
67 return ((void __RPC_FAR *)malloc(cBytes));
71 midl_user_free(void __RPC_FAR * p)
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().
82 getservername(char **snp, unsigned int snSize)
88 RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE,
90 if (code != ERROR_SUCCESS)
92 code = RegQueryValueEx(parmKey, "Gateway", NULL, NULL, *snp, &snSize);
94 if (code == ERROR_SUCCESS)
97 /* No gateway name in registry; use ourself */
102 send_key(afs_uuid_t uuid, char sessionKey[8])
105 char *stringBinding = NULL;
106 ULONG authnLevel, authnSvc;
107 char serverName[256];
108 char *serverNamep = serverName;
110 BOOL encryptionOff = FALSE;
113 /* Encryption on by default */
114 if (GetEnvironmentVariable("AFS_RPC_ENCRYPT", encrypt, sizeof(encrypt)))
115 if (!_stricmp(encrypt, "OFF"))
116 encryptionOff = TRUE;
118 /* Protocol sequence is local by default */
119 if (!GetEnvironmentVariable("AFS_RPC_PROTSEQ", protseq, sizeof(protseq)))
120 strcpy(protseq, "ncalrpc");
123 getservername(&serverNamep, sizeof(serverName));
125 status = RpcStringBindingCompose("", /* obj uuid */
126 protseq, serverNamep, "", /* endpoint */
127 "", /* protocol options */
129 if (status != RPC_S_OK)
132 status = RpcBindingFromStringBinding(stringBinding, &hAfsHandle);
133 if (status != RPC_S_OK)
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.
141 status = RpcEpResolveBinding(hAfsHandle, afsrpc_v1_0_c_ifspec);
142 if (status != RPC_S_OK)
146 authnLevel = RPC_C_AUTHN_LEVEL_NONE;
147 authnSvc = RPC_C_AUTHN_WINNT;
149 authnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
150 authnSvc = RPC_C_AUTHN_WINNT;
154 RpcBindingSetAuthInfo(hAfsHandle, NULL, authnLevel, authnSvc, NULL,
156 if (status != RPC_S_OK)
160 status = AFSRPC_SetToken(uuid, sessionKey);
163 status = RpcExceptionCode();
165 RpcEndExcept cleanup:if (stringBinding)
166 RpcStringFree(&stringBinding);
168 if (hAfsHandle != NULL)
169 RpcBindingFree(&hAfsHandle);
175 receive_key(afs_uuid_t uuid, char sessionKey[8])
178 char *stringBinding = NULL;
179 ULONG authnLevel, authnSvc;
180 char serverName[256];
181 char *serverNamep = serverName;
183 BOOL encryptionOff = FALSE;
186 /* Encryption on by default */
187 if (GetEnvironmentVariable("AFS_RPC_ENCRYPT", encrypt, sizeof(encrypt)))
188 if (!_stricmp(encrypt, "OFF"))
189 encryptionOff = TRUE;
191 /* Protocol sequence is local by default */
192 if (!GetEnvironmentVariable("AFS_RPC_PROTSEQ", protseq, sizeof(protseq)))
193 strcpy(protseq, "ncalrpc");
196 getservername(&serverNamep, sizeof(serverName));
198 status = RpcStringBindingCompose("", /* obj uuid */
199 protseq, serverNamep, "", /* endpoint */
200 "", /* protocol options */
202 if (status != RPC_S_OK)
205 status = RpcBindingFromStringBinding(stringBinding, &hAfsHandle);
206 if (status != RPC_S_OK)
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.
214 status = RpcEpResolveBinding(hAfsHandle, afsrpc_v1_0_c_ifspec);
215 if (status != RPC_S_OK)
219 authnLevel = RPC_C_AUTHN_LEVEL_NONE;
220 authnSvc = RPC_C_AUTHN_WINNT;
222 authnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
223 authnSvc = RPC_C_AUTHN_WINNT;
227 RpcBindingSetAuthInfo(hAfsHandle, NULL, authnLevel, authnSvc, NULL,
229 if (status != RPC_S_OK)
233 status = AFSRPC_GetToken(uuid, sessionKey);
236 status = RpcExceptionCode();
238 RpcEndExcept cleanup:if (stringBinding)
239 RpcStringFree(&stringBinding);
241 if (hAfsHandle != NULL)
242 RpcBindingFree(&hAfsHandle);
248 ktc_SetToken(struct ktc_principal *server, struct ktc_token *token,
249 struct ktc_principal *client, int flags)
251 struct ViceIoctl iob;
252 char tbuffer[TBUFFERSIZE];
255 struct ClearToken ct;
260 HANDLE ktcMutex = NULL;
262 if (token->ticketLen < MINKTCTICKETLEN
263 || token->ticketLen > MAXKTCTICKETLEN)
266 if (strcmp(server->name, "afs")) {
267 return SetLocalToken(server, token, client, flags);
273 memcpy(tp, &token->ticketLen, sizeof(token->ticketLen));
274 tp += sizeof(token->ticketLen);
275 len = sizeof(token->ticketLen);
278 if (len + token->ticketLen > TBUFFERSIZE)
280 memcpy(tp, token->ticket, token->ticketLen);
281 tp += token->ticketLen;
282 len += token->ticketLen;
285 ct.AuthHandle = token->kvno;
287 * Instead of sending the session key in the clear, we zero it,
288 * and send it later, via RPC, encrypted.
290 #ifndef AFS_WIN95_ENV
292 * memcpy(ct.HandShakeKey, &token->sessionKey, sizeof(token->sessionKey));
294 memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
296 memcpy(ct.HandShakeKey, &token->sessionKey, sizeof(token->sessionKey));
298 ct.BeginTimestamp = token->startTime;
299 ct.EndTimestamp = token->endTime;
300 if (ct.BeginTimestamp == 0)
301 ct.BeginTimestamp = 1;
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 */
308 /* size of clear token */
309 if (len + sizeof(temp) > TBUFFERSIZE)
311 temp = sizeof(struct ClearToken);
312 memcpy(tp, &temp, sizeof(temp));
316 /* clear token itself */
317 if (len + sizeof(ct) > TBUFFERSIZE)
319 memcpy(tp, &ct, sizeof(ct));
323 /* flags; on NT there is no setpag flag, but there is an
324 * integrated logon flag */
325 if (len + sizeof(temp) > TBUFFERSIZE)
327 temp = ((flags & AFS_SETTOK_LOGON) ? PIOCTL_LOGON : 0);
328 memcpy(tp, &temp, sizeof(temp));
333 temp = (int)strlen(server->cell) + 1;
334 if (len + temp > TBUFFERSIZE ||
335 temp > MAXKTCREALMLEN)
337 strcpy(tp, server->cell);
342 temp = (int)strlen(client->name) + 1;
343 if (len + temp > TBUFFERSIZE ||
344 temp > MAXKTCNAMELEN)
346 strcpy(tp, client->name);
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)
356 temp = (int)strlen(client->smbname) + 1;
358 len + temp > TBUFFERSIZE ||
359 temp > MAXKTCNAMELEN)
361 strcpy(tp, client->smbname);
367 if (len + sizeof(uuid) > TBUFFERSIZE)
369 status = UuidCreate((UUID *) & uuid);
370 memcpy(tp, &uuid, sizeof(uuid));
374 #ifndef AFS_WIN95_ENV
375 ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
376 if (ktcMutex == NULL)
377 return KTC_PIOCTLFAIL;
378 if (GetLastError() == ERROR_ALREADY_EXISTS) {
379 if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
380 CloseHandle(ktcMutex);
381 return KTC_PIOCTLFAIL;
385 /* RPC to send session key */
386 status = send_key(uuid, token->sessionKey.data);
387 if (status != RPC_S_OK) {
389 strcpy(rpcErr, "RPC failure in AFS gateway");
391 DceErrorInqText(status, rpcErr);
392 if (status == RPC_S_SERVER_UNAVAILABLE ||
393 status == EPT_S_NOT_REGISTERED) {
394 ReleaseMutex(ktcMutex);
395 CloseHandle(ktcMutex);
398 ReleaseMutex(ktcMutex);
399 CloseHandle(ktcMutex);
403 #endif /* AFS_WIN95_ENV */
405 /* set up for pioctl */
407 iob.in_size = (long)(tp - tbuffer);
409 iob.out_size = sizeof(tbuffer);
411 code = pioctl(0, VIOCSETTOK, &iob, 0);
413 #ifndef AFS_WIN95_ENV
414 ReleaseMutex(ktcMutex);
415 CloseHandle(ktcMutex);
416 #endif /* AFS_WIN95_ENV */
422 else if (errno == ENODEV)
424 else if (errno == EINVAL)
427 return KTC_PIOCTLFAIL;
429 return KTC_PIOCTLFAIL;
436 ktc_SetTokenEx(struct ktc_setTokenData *token)
438 /* Not yet implemented */
439 return KTC_PIOCTLFAIL;
443 ktc_GetToken(struct ktc_principal *server, struct ktc_token *token,
444 int tokenLen, struct ktc_principal *client)
446 struct ViceIoctl iob;
447 char tbuffer[TBUFFERSIZE];
452 struct ClearToken ct;
459 HANDLE ktcMutex = NULL;
463 /* check to see if the user is requesting tokens for a principal
464 * other than afs. If so, check the local token cache.
466 if (strcmp(server->name, "afs")) {
467 return GetLocalToken(server, token, tokenLen, client);
471 len = strlen(server->cell) + 1;
472 strcpy(tp, server->cell);
476 status = UuidCreate((UUID *) & uuid);
477 memcpy(tp, &uuid, sizeof(uuid));
482 iob.in_size = (long)(tp - tbuffer);
484 iob.out_size = sizeof(tbuffer);
486 #ifndef AFS_WIN95_ENV
487 ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
488 if (ktcMutex == NULL)
489 return KTC_PIOCTLFAIL;
490 if (GetLastError() == ERROR_ALREADY_EXISTS) {
491 if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
492 CloseHandle(ktcMutex);
493 return KTC_PIOCTLFAIL;
496 #endif /* AFS_WIN95_ENV */
498 code = pioctl(0, VIOCNEWGETTOK, &iob, 0);
500 #ifndef AFS_WIN95_ENV
501 ReleaseMutex(ktcMutex);
502 CloseHandle(ktcMutex);
503 #endif /* AFS_WIN95_ENV */
507 else if (errno == ENODEV)
509 else if (errno == EINVAL)
511 else if (errno == EDOM)
514 return KTC_PIOCTLFAIL;
516 return KTC_PIOCTLFAIL;
518 #ifndef AFS_WIN95_ENV
519 /* get rid of RPC for win95 build */
520 /* RPC to receive session key */
521 status = receive_key(uuid, token->sessionKey.data);
523 ReleaseMutex(ktcMutex);
524 CloseHandle(ktcMutex);
526 if (status != RPC_S_OK) {
528 strcpy(rpcErr, "RPC failure in AFS gateway");
530 DceErrorInqText(status, rpcErr);
531 if (status == RPC_S_SERVER_UNAVAILABLE
532 || status == EPT_S_NOT_REGISTERED)
537 #endif /* AFS_WIN95_ENV */
542 memcpy(&ticketLen, cp, sizeof(ticketLen));
543 cp += sizeof(ticketLen);
544 len = sizeof(ticketLen);
546 /* remember where ticket is and skip over it */
547 if (len + ticketLen > TBUFFERSIZE ||
548 len + ticketLen > iob.out_size)
554 /* size of clear token */
555 if (len + sizeof(temp) > TBUFFERSIZE ||
556 len + sizeof(temp) > iob.out_size)
558 memcpy(&temp, cp, sizeof(temp));
561 if (temp != sizeof(ct))
565 if (len + temp > TBUFFERSIZE ||
566 len + temp > iob.out_size)
568 memcpy(&ct, cp, temp);
572 /* skip over primary flag */
573 if (len + sizeof(temp) > TBUFFERSIZE ||
574 len + sizeof(temp) > iob.out_size)
579 /* remember cell name and skip over it */
581 cellNameSize = (int)strlen(cp);
582 if (len + cellNameSize + 1 > TBUFFERSIZE ||
583 len + cellNameSize + 1 > iob.out_size)
585 cp += cellNameSize + 1;
586 len += cellNameSize + 1;
588 /* user name is here */
590 /* check that ticket will fit
591 * this compares the size of the ktc_token allocated by the app
592 * which might be smaller than the current definition of MAXKTCTICKETLEN
594 maxLen = tokenLen - sizeof(struct ktc_token) + MAXKTCTICKETLEN;
595 if (maxLen < ticketLen)
598 /* set return values */
599 memcpy(token->ticket, ticketP, ticketLen);
600 token->startTime = ct.BeginTimestamp;
601 token->endTime = ct.EndTimestamp;
602 if (ct.AuthHandle == -1)
604 token->kvno = ct.AuthHandle;
605 #ifndef AFS_WIN95_ENV
607 * Session key has already been set via RPC
610 memcpy(&token->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
611 #endif /* AFS_WIN95_ENV */
612 token->ticketLen = ticketLen;
614 strcpy(client->name, cp);
615 client->instance[0] = '\0';
616 strcpy(client->cell, cellName);
623 * Get a token, given the cell that we need to get information for
626 * The name of the cell we're getting the token for - if NULL, we'll
627 * get information for the primary cell
630 ktc_GetTokenEx(char *cellName, struct ktc_setTokenData **tokenSet) {
631 return KTC_PIOCTLFAIL;
635 ktc_ListTokens(int cellNum, int *cellNumP, struct ktc_principal *server)
637 struct ViceIoctl iob;
638 char tbuffer[TBUFFERSIZE];
641 int newIter, ticketLen, temp;
643 HANDLE ktcMutex = NULL;
645 #ifndef AFS_WIN95_ENV
646 ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
647 if (ktcMutex == NULL)
648 return KTC_PIOCTLFAIL;
649 if (GetLastError() == ERROR_ALREADY_EXISTS) {
650 if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
651 CloseHandle(ktcMutex);
652 return KTC_PIOCTLFAIL;
655 #endif /* AFS_WIN95_ENV */
660 memcpy(tp, &cellNum, sizeof(cellNum));
661 tp += sizeof(cellNum);
665 iob.in_size = (long)(tp - tbuffer);
667 iob.out_size = sizeof(tbuffer);
669 code = pioctl(0, VIOCGETTOK, &iob, 0);
671 #ifndef AFS_WIN95_ENV
672 ReleaseMutex(ktcMutex);
673 CloseHandle(ktcMutex);
674 #endif /* AFS_WIN95_ENV */
680 else if (errno == ENODEV)
682 else if (errno == EINVAL)
684 else if (errno == EDOM)
687 return KTC_PIOCTLFAIL;
689 return KTC_PIOCTLFAIL;
695 memcpy(&newIter, cp, sizeof(newIter));
696 cp += sizeof(newIter);
697 len = sizeof(newIter);
700 if (len + sizeof(ticketLen) > TBUFFERSIZE ||
701 len + sizeof(ticketLen) > iob.out_size)
703 memcpy(&ticketLen, cp, sizeof(ticketLen));
704 cp += sizeof(ticketLen);
705 len += sizeof(ticketLen);
707 /* skip over ticket */
711 /* clear token size */
712 if (len + sizeof(temp) > TBUFFERSIZE ||
713 len + sizeof(temp) > iob.out_size)
715 memcpy(&temp, cp, sizeof(temp));
718 if (temp != sizeof(struct ClearToken))
721 /* skip over clear token */
722 cp += sizeof(struct ClearToken);
723 len += sizeof(struct ClearToken);
725 /* skip over primary flag */
728 if (len > TBUFFERSIZE ||
732 /* cell name is here */
734 /* set return values */
735 if (len + temp > TBUFFERSIZE ||
736 temp > MAXKTCREALMLEN)
738 strcpy(server->cell, cp);
739 server->instance[0] = '\0';
740 strcpy(server->name, "afs");
747 ktc_ForgetToken(struct ktc_principal *server)
749 struct ViceIoctl iob;
750 char tbuffer[TBUFFERSIZE];
753 HANDLE ktcMutex = NULL;
755 if (strcmp(server->name, "afs")) {
756 return ForgetOneLocalToken(server);
758 #ifndef AFS_WIN95_ENV
759 ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
760 if (ktcMutex == NULL)
761 return KTC_PIOCTLFAIL;
762 if (GetLastError() == ERROR_ALREADY_EXISTS) {
763 if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
764 CloseHandle(ktcMutex);
765 return KTC_PIOCTLFAIL;
768 #endif /* AFS_WIN95_ENV */
773 strcpy(tp, server->cell);
774 tp += strlen(tp) + 1;
778 iob.in_size = (long)(tp - tbuffer);
780 iob.out_size = sizeof(tbuffer);
782 code = pioctl(0, VIOCDELTOK, &iob, 0);
783 #ifndef AFS_WIN95_ENV
784 ReleaseMutex(ktcMutex);
785 CloseHandle(ktcMutex);
786 #endif /* AFS_WIN95_ENV */
791 else if (errno == EDOM)
793 else if (errno == ENODEV)
796 return KTC_PIOCTLFAIL;
798 return KTC_PIOCTLFAIL;
804 ktc_ForgetAllTokens()
806 struct ViceIoctl iob;
807 char tbuffer[TBUFFERSIZE];
809 HANDLE ktcMutex = NULL;
811 (void)ForgetLocalTokens();
813 #ifndef AFS_WIN95_ENV
814 ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
815 if (ktcMutex == NULL)
816 return KTC_PIOCTLFAIL;
817 if (GetLastError() == ERROR_ALREADY_EXISTS) {
818 if (WaitForSingleObject(ktcMutex, INFINITE) != WAIT_OBJECT_0) {
819 CloseHandle(ktcMutex);
820 return KTC_PIOCTLFAIL;
823 #endif /* AFS_WIN95_ENV */
829 iob.out_size = sizeof(tbuffer);
831 code = pioctl(0, VIOCDELALLTOK, &iob, 0);
832 #ifndef AFS_WIN95_ENV
833 ReleaseMutex(ktcMutex);
834 CloseHandle(ktcMutex);
835 #endif /* AFS_WIN95_ENV */
841 return KTC_PIOCTLFAIL;
843 return KTC_PIOCTLFAIL;
855 #define MAXLOCALTOKENS 4
859 struct ktc_principal server;
860 struct ktc_principal client;
861 struct ktc_token token;
862 } local_tokens[MAXLOCALTOKENS] = {
866 SetLocalToken(struct ktc_principal *aserver, struct ktc_token *atoken,
867 struct ktc_principal *aclient, afs_int32 flags)
873 for (i = 0; i < MAXLOCALTOKENS; i++)
874 if (local_tokens[i].valid) {
875 if ((strcmp(local_tokens[i].server.name, aserver->name) == 0)
876 && (strcmp(local_tokens[i].server.instance, aserver->instance)
878 && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
879 found = i; /* replace existing entry */
882 } else if (found == -1)
883 found = i; /* remember empty slot but keep looking for a match */
888 memcpy(&local_tokens[found].token, atoken, sizeof(struct ktc_token));
889 memcpy(&local_tokens[found].server, aserver,
890 sizeof(struct ktc_principal));
891 memcpy(&local_tokens[found].client, aclient,
892 sizeof(struct ktc_principal));
893 local_tokens[found].valid = 1;
900 GetLocalToken(struct ktc_principal *aserver, struct ktc_token *atoken,
901 int atokenLen, struct ktc_principal *aclient)
906 for (i = 0; i < MAXLOCALTOKENS; i++)
907 if (local_tokens[i].valid
908 && (strcmp(local_tokens[i].server.name, aserver->name) == 0)
909 && (strcmp(local_tokens[i].server.instance, aserver->instance) ==
911 && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
912 memcpy(atoken, &local_tokens[i].token,
913 min(atokenLen, sizeof(struct ktc_token)));
914 memcpy(aclient, &local_tokens[i].client,
915 sizeof(struct ktc_principal));
930 for (i = 0; i < MAXLOCALTOKENS; i++) {
931 local_tokens[i].valid = 0;
932 memset(&local_tokens[i].token.sessionKey, 0,
933 sizeof(struct ktc_encryptionKey));
941 ForgetOneLocalToken(struct ktc_principal *aserver)
946 for (i = 0; i < MAXLOCALTOKENS; i++) {
947 if (local_tokens[i].valid
948 && (strcmp(local_tokens[i].server.name, aserver->name) == 0)
949 && (strcmp(local_tokens[i].server.instance, aserver->instance) ==
951 && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
952 local_tokens[i].valid = 0;
953 memset(&local_tokens[i].token.sessionKey, 0,
954 sizeof(struct ktc_encryptionKey));
964 * An iterator which can list all cells with tokens in the cache
966 * This function may be used to list the names of all cells for which
967 * tokens exist in the current cache. The first time that it is called,
968 * prevIndex should be set to 0. On all subsequent calls, prevIndex
969 * should be set to the value returned in newIndex by the last call
970 * to the function. Note that there is no guarantee that the index value
971 * is monotonically increasing.
974 * The index returned by the last call, or 0 if this is the first
975 * call in an iteration
977 * A pointer to an int which, upon return, will hold the next value
980 * A pointer to a char * which, upon return, will hold a cellname.
981 * This must be freed by the caller using free()
985 ktc_ListTokensEx(int prevIndex, int *newIndex, char **cellName) {
987 return KTC_PIOCTLFAIL;