kauth: fix clock skew detection
[openafs.git] / src / kauth / knfs.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 /*
11  * ALL RIGHTS RESERVED
12  */
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16 #include <afs/stds.h>
17
18 #include <roken.h>
19
20 #include <ctype.h>
21
22 #include <afs/vice.h>
23 #include <afs/cmd.h>
24 #include <afs/auth.h>
25 #include <afs/afsutil.h>
26 #include <afs/sys_prototypes.h>
27
28 /*
29 Modifications:
30
31 29 Oct 1992 Patch GetTokens to print something reasonable when there are no tokens.
32
33 */
34
35 /* this is a structure used to communicate with the afs cache mgr, but is
36  * otherwise irrelevant, or worse.
37  */
38 struct ClearToken {
39     afs_int32 AuthHandle;
40     char HandShakeKey[8];
41     afs_int32 ViceId;
42     afs_int32 BeginTimestamp;
43     afs_int32 EndTimestamp;
44 };
45
46
47 static int
48 SetSysname(afs_int32 ahost, afs_int32 auid, char *sysname)
49 {
50     afs_int32 code;
51     afs_int32 pheader[6];
52     char space[1200], *tp;
53     struct ViceIoctl blob;
54     afs_int32 setp = 1;
55
56     /* otherwise we've got the token, now prepare to build the pioctl args */
57     pheader[0] = ahost;
58     pheader[1] = auid;
59     pheader[2] = 0;             /* group low */
60     pheader[3] = 0;             /* group high */
61     pheader[4] = 38 /*VIOC_AFS_SYSNAME */ ;     /* sysname pioctl index */
62     pheader[5] = 1;             /* NFS protocol exporter # */
63
64     /* copy stuff in */
65     memcpy(space, pheader, sizeof(pheader));
66     tp = space + sizeof(pheader);
67
68     /* finally setup the pioctl call's parameters */
69     blob.in_size = sizeof(pheader);
70     blob.in = space;
71     blob.out_size = 0;
72     blob.out = NULL;
73     memcpy(tp, &setp, sizeof(afs_int32));
74     tp += sizeof(afs_int32);
75     strcpy(tp, sysname);
76     blob.in_size += sizeof(afs_int32) + strlen(sysname) + 1;
77     tp += strlen(sysname);
78     *(tp++) = '\0';
79     code = pioctl(NULL, _VICEIOCTL(99), &blob, 0);
80     if (code) {
81         code = errno;
82     }
83     return code;
84 }
85
86
87 static int
88 GetTokens(afs_int32 ahost, afs_int32 auid)
89 {
90     struct ViceIoctl iob;
91     afs_int32 pheader[6];
92     char tbuffer[1024];
93     afs_int32 code = 0;
94     int index, newIndex;
95     char *stp;                  /* secret token ptr */
96     struct ClearToken ct;
97     char *tp;
98     afs_int32 temp, gotit = 0;
99     int maxLen;                 /* biggest ticket we can copy */
100     int tktLen;                 /* server ticket length */
101     time_t tokenExpireTime;
102     char UserName[MAXKTCNAMELEN + MAXKTCNAMELEN];
103     struct ktc_token token;
104     struct ktc_principal clientName;
105     time_t current_time;
106     char *expireString;
107
108     current_time = time(0);
109
110     /* otherwise we've got the token, now prepare to build the pioctl args */
111     pheader[0] = ahost;
112     pheader[1] = auid;
113     pheader[2] = 0;             /* group low */
114     pheader[3] = 0;             /* group high */
115     pheader[4] = 8;             /* gettoken pioctl index */
116     pheader[5] = 1;             /* NFS protocol exporter # */
117
118     for (index = 0; index < 200; index++) {     /* sanity check in case pioctl fails */
119         code = ktc_ListTokens(index, &newIndex, &clientName);
120         if (code) {
121             if (code == KTC_NOENT) {
122                 /* all done */
123                 if (!gotit)
124                     printf("knfs: there are no tokens here.\n");
125                 code = 0;
126             }
127             break;              /* done, but failed */
128         }
129         if (strcmp(clientName.name, "afs") != 0)
130             continue;           /* wrong ticket service */
131
132         /* copy stuff in */
133         memcpy(tbuffer, pheader, sizeof(pheader));
134         tp = tbuffer + sizeof(pheader);
135         memcpy(tp, &index, sizeof(afs_int32));
136         tp += sizeof(afs_int32);
137         iob.in = tbuffer;
138         iob.in_size = sizeof(afs_int32) + sizeof(pheader);
139         iob.out = tbuffer;
140         iob.out_size = sizeof(tbuffer);
141         code = pioctl(NULL, _VICEIOCTL(99), &iob, 0);
142         if (code < 0 && errno == EDOM)
143             return KTC_NOENT;
144         else if (code == 0) {
145             /* check to see if this is the right cell/realm */
146             tp = tbuffer;
147             memcpy(&temp, tp, sizeof(afs_int32));       /* get size of secret token */
148             tktLen = temp;      /* remember size of ticket */
149             tp += sizeof(afs_int32);
150             stp = tp;           /* remember where ticket is, for later */
151             tp += temp;         /* skip ticket for now */
152             memcpy(&temp, tp, sizeof(afs_int32));       /* get size of clear token */
153             if (temp != sizeof(struct ClearToken))
154                 return KTC_ERROR;
155             tp += sizeof(afs_int32);    /* skip length */
156             memcpy(&ct, tp, temp);      /* copy token for later use */
157             tp += temp;         /* skip clear token itself */
158             tp += sizeof(afs_int32);    /* skip primary flag */
159             /* tp now points to the cell name */
160             if (strcmp(tp, clientName.cell) == 0) {
161                 /* closing in now, we've got the right cell */
162                 gotit = 1;
163                 maxLen =
164                     sizeof(token) - sizeof(struct ktc_token) +
165                     MAXKTCTICKETLEN;
166                 if (tktLen < 0 || tktLen > maxLen)
167                     return KTC_TOOBIG;
168                 memcpy(token.ticket, stp, tktLen);
169                 token.startTime = ct.BeginTimestamp;
170                 token.endTime = ct.EndTimestamp;
171                 if (ct.AuthHandle == -1)
172                     ct.AuthHandle = 999;
173                 token.kvno = ct.AuthHandle;
174                 memcpy(&token.sessionKey, ct.HandShakeKey,
175                        sizeof(struct ktc_encryptionKey));
176                 token.ticketLen = tktLen;
177                 if ((token.kvno == 999) ||      /* old style bcrypt ticket */
178                     (ct.BeginTimestamp &&       /* new w/ prserver lookup */
179                      (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1))) {
180                     sprintf(clientName.name, "AFS ID %d", ct.ViceId);
181                     clientName.instance[0] = 0;
182                 } else {
183                     sprintf(clientName.name, "Unix UID %d", ct.ViceId);
184                     clientName.instance[0] = 0;
185                 }
186                 strlcpy(clientName.cell, tp, sizeof(clientName.cell));
187
188                 tokenExpireTime = token.endTime;
189                 strlcpy(UserName, clientName.name, sizeof(UserName));
190                 if (clientName.instance[0] != 0) {
191                     strlcat(UserName, ".", sizeof(UserName));
192                     strlcat(UserName, clientName.instance, sizeof(UserName));
193                 }
194                 if (UserName[0] == 0)
195                     printf("Tokens");
196                 else if (strncmp(UserName, "AFS ID", 6) == 0) {
197                     printf("User's (%s) tokens", UserName);
198                 } else if (strncmp(UserName, "Unix UID", 8) == 0) {
199                     printf("Tokens");
200                 } else
201                     printf("User %s's tokens", UserName);
202                 printf(" for %s%s%s@%s ", clientName.name,
203                        clientName.instance[0] ? "." : "", clientName.instance,
204                        clientName.cell);
205                 if (tokenExpireTime <= current_time)
206                     printf("[>> Expired <<]\n");
207                 else {
208                     expireString = ctime(&tokenExpireTime);
209                     expireString += 4;  /*Move past the day of week */
210                     expireString[12] = '\0';
211                     printf("[Expires %s]\n", expireString);
212                 }
213
214             }
215         }
216     }
217     return code;
218 }
219
220
221 static int
222 NFSUnlog(afs_int32 ahost, afs_int32 auid)
223 {
224     afs_int32 code;
225     afs_int32 pheader[6];
226     char space[1200];
227     struct ViceIoctl blob;
228
229     /* otherwise we've got the token, now prepare to build the pioctl args */
230     pheader[0] = ahost;
231     pheader[1] = auid;
232     pheader[2] = 0;             /* group low */
233     pheader[3] = 0;             /* group high */
234     pheader[4] = 9;             /* unlog pioctl index */
235     pheader[5] = 1;             /* NFS protocol exporter # */
236
237     /* copy stuff in */
238     memcpy(space, pheader, sizeof(pheader));
239
240     /* finally setup the pioctl call's parameters */
241     blob.in_size = sizeof(pheader);
242     blob.in = space;
243     blob.out_size = 0;
244     blob.out = NULL;
245     code = pioctl(NULL, _VICEIOCTL(99), &blob, 0);
246     if (code) {
247         code = errno;
248     }
249     return code;
250 }
251
252 /* Copy the AFS service token into the kernel for a particular host and user */
253 static int
254 NFSCopyToken(afs_int32 ahost, afs_int32 auid)
255 {
256     struct ktc_principal client, server;
257     struct ktc_token theTicket;
258     afs_int32 code;
259     afs_int32 pheader[6];
260     char space[1200];
261     struct ClearToken ct;
262     afs_int32 index, newIndex;
263     afs_int32 temp;             /* for bcopy */
264     char *tp;
265     struct ViceIoctl blob;
266
267     for (index = 0;; index = newIndex) {
268         code = ktc_ListTokens(index, &newIndex, &server);
269         if (code) {
270             if (code == KTC_NOENT) {
271                 /* all done */
272                 code = 0;
273             }
274             break;              /* done, but failed */
275         }
276         if (strcmp(server.name, "afs") != 0)
277             continue;           /* wrong ticket service */
278         code = ktc_GetToken(&server, &theTicket, sizeof(theTicket), &client);
279         if (code)
280             return code;
281
282         /* otherwise we've got the token, now prepare to build the pioctl args */
283         pheader[0] = ahost;
284         pheader[1] = auid;
285         pheader[2] = 0;         /* group low */
286         pheader[3] = 0;         /* group high */
287         pheader[4] = 3;         /* set token pioctl index */
288         pheader[5] = 1;         /* NFS protocol exporter # */
289
290         /* copy in the header */
291         memcpy(space, pheader, sizeof(pheader));
292         tp = space + sizeof(pheader);
293         /* copy in the size of the encrypted part */
294         memcpy(tp, &theTicket.ticketLen, sizeof(afs_int32));
295         tp += sizeof(afs_int32);
296         /* copy in the ticket itself */
297         memcpy(tp, theTicket.ticket, theTicket.ticketLen);
298         tp += theTicket.ticketLen;
299         /* copy in "clear token"'s size */
300         temp = sizeof(struct ClearToken);
301         memcpy(tp, &temp, sizeof(afs_int32));
302         tp += sizeof(afs_int32);
303         /* create the clear token and copy *it* in */
304         ct.AuthHandle = theTicket.kvno; /* where we hide the key version # */
305         memcpy(ct.HandShakeKey, &theTicket.sessionKey,
306                sizeof(ct.HandShakeKey));
307
308         ct.ViceId = auid;
309         ct.BeginTimestamp = theTicket.startTime;
310         ct.EndTimestamp = theTicket.endTime;
311         memcpy(tp, &ct, sizeof(ct));
312         tp += sizeof(ct);
313         /* copy in obsolete primary flag */
314         temp = 0;
315         memcpy(tp, &temp, sizeof(afs_int32));
316         tp += sizeof(afs_int32);
317         /* copy in cell name, null terminated */
318         strcpy(tp, server.cell);
319         tp += strlen(server.cell) + 1;
320
321         /* finally setup the pioctl call's parameters */
322         blob.in_size = tp - space;
323         blob.in = space;
324         blob.out_size = 0;
325         blob.out = NULL;
326         code = pioctl(NULL, _VICEIOCTL(99), &blob, 0);
327         if (code) {
328             code = errno;
329             break;
330         }
331     }
332     return code;
333 }
334
335 static int
336 cmdproc(struct cmd_syndesc *as, void *arock)
337 {
338     struct hostent *the;
339     char *tp, *sysname = 0;
340     afs_int32 uid, addr;
341     afs_int32 code;
342
343     the = (struct hostent *)
344         hostutil_GetHostByName(tp = as->parms[0].items->data);
345     if (!the) {
346         printf("knfs: unknown host '%s'.\n", tp);
347         return -1;
348     }
349     memcpy(&addr, the->h_addr, sizeof(afs_int32));
350     uid = -1;
351     if (as->parms[1].items) {
352         code = util_GetInt32(tp = as->parms[1].items->data, &uid);
353         if (code) {
354             printf("knfs: can't parse '%s' as a number (UID)\n", tp);
355             return code;
356         }
357     } else
358         uid = -1;               /* means wildcard: match any user on this host */
359
360     /*
361      * If not "-id" is passed then we use the getuid() id, unless it's root
362      * that is doing it in which case we only authenticate as "system:anyuser"
363      * as it's appropriate for root. (The cm handles conversions from 0 to
364      * "afs_nobody"!)
365      */
366     if (uid == -1) {
367         uid = getuid();
368     }
369
370     if (as->parms[2].items) {
371         sysname = as->parms[2].items->data;
372     }
373
374     if (as->parms[4].items) {
375         /* tokens specified */
376         code = GetTokens(addr, uid);
377         if (code) {
378             if (code == ENOEXEC)
379                 printf
380                     ("knfs: Translator in 'passwd sync' mode; remote uid must be the same as local uid\n");
381             else
382                 printf("knfs: failed to get tokens for uid %d (code %d)\n",
383                        uid, code);
384         }
385         return code;
386     }
387
388     /* finally, parsing is done, make the call */
389     if (as->parms[3].items) {
390         /* unlog specified */
391         code = NFSUnlog(addr, uid);
392         if (code) {
393             if (code == ENOEXEC)
394                 printf
395                     ("knfs: Translator in 'passwd sync' mode; remote uid must be the same as local uid\n");
396             else
397                 printf("knfs: failed to unlog (code %d)\n", code);
398         }
399     } else {
400         code = NFSCopyToken(addr, uid);
401         if (code) {
402             if (code == ENOEXEC)
403                 printf
404                     ("knfs: Translator in 'passwd sync' mode; remote uid must be the same as local uid\n");
405             else
406                 printf("knfs: failed to copy tokens (code %d)\n", code);
407         }
408         if (sysname) {
409             code = SetSysname(addr, uid, sysname);
410             if (code) {
411                 printf("knfs: failed to set client's @sys to %s (code %d)\n",
412                        sysname, code);
413             }
414         }
415     }
416     return code;
417 }
418
419 #include "AFS_component_version_number.c"
420
421 int
422 main(int argc, char **argv)
423 {
424     struct cmd_syndesc *ts;
425     afs_int32 code;
426
427 #ifdef  AFS_AIX32_ENV
428     /*
429      * The following signal action for AIX is necessary so that in case of a
430      * crash (i.e. core is generated) we can include the user's data section
431      * in the core dump. Unfortunately, by default, only a partial core is
432      * generated which, in many cases, isn't too useful.
433      */
434     struct sigaction nsa;
435
436     sigemptyset(&nsa.sa_mask);
437     nsa.sa_handler = SIG_DFL;
438     nsa.sa_flags = SA_FULLDUMP;
439     sigaction(SIGABRT, &nsa, NULL);
440     sigaction(SIGSEGV, &nsa, NULL);
441 #endif
442
443     ts = cmd_CreateSyntax(NULL, cmdproc, NULL, 0, "copy tickets for NFS");
444     cmd_AddParm(ts, "-host", CMD_SINGLE, CMD_REQUIRED, "host name");
445     cmd_AddParm(ts, "-id", CMD_SINGLE, CMD_OPTIONAL, "user ID (decimal)");
446     cmd_AddParm(ts, "-sysname", CMD_SINGLE, CMD_OPTIONAL,
447                 "host's '@sys' value");
448     cmd_AddParm(ts, "-unlog", CMD_FLAG, CMD_OPTIONAL, "unlog remote user");
449     cmd_AddParm(ts, "-tokens", CMD_FLAG, CMD_OPTIONAL,
450                 "display all tokens for remote [host,id]");
451
452     code = cmd_Dispatch(argc, argv);
453     return code;
454 }