windows-force-krb524-20030921
[openafs.git] / src / WINNT / aklog / aklog.c
1 /* 
2  *  Copyright (C) 1989,2004 by the Massachusetts Institute of Technology
3  * 
4  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
5  * distribute this software and its documentation for any purpose and
6  * without fee is hereby granted, provided that the above copyright
7  * notice appear in all copies and that both that copyright notice and
8  * this permission notice appear in supporting documentation, and that
9  * the name of M.I.T. not be used in advertising or publicity pertaining
10  * to distribution of the software without specific, written prior
11  * permission.  M.I.T. makes no representations about the suitability of
12  * this software for any purpose.  It is provided "as is" without express
13  * or implied warranty.
14  */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <errno.h>
23 #include <afs/stds.h>
24 #include <krb.h>
25 #include <krb5.h>
26 #include <afs/ptserver.h>
27
28 #ifdef WIN32
29 #include <windows.h>
30
31 #include <cm_config.h>
32 #include <auth.h>
33 #include <cellconfig.h>
34 #include <pioctl_nt.h>
35 #include <smb_iocons.h>
36
37 #define stat _stat
38 #define lstat stat
39 #define __S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
40 #define S_ISDIR(mode)          __S_ISTYPE((mode), _S_IFDIR)
41
42 #define DONT_HAVE_GET_AD_TKT
43 #define MAXSYMLINKS 255
44
45 /* Win32 uses get_krb_err_txt_entry(status) instead of krb_err_txt[status],
46 * so we use a bit of indirection like the GNU CVS sources.
47 */
48 #define krb_err_text(status) get_krb_err_txt_entry(status)
49
50 #define DRIVECOLON ':'          /* Drive letter separator */
51 #define BDIR '\\'               /* Other character that divides directories */
52
53 static int 
54 readlink(char *path, char *buf, int buffers)
55 {
56         return -1;
57 }
58
59 char * getcwd(char*, size_t);
60
61 static long 
62 get_cellconfig_callback(void *cellconfig, struct sockaddr_in *addrp, char *namep)
63 {
64         struct afsconf_cell *cc = (struct afsconf_cell *) cellconfig;
65
66         cc->hostAddr[cc->numServers] = *addrp;
67         strcpy(cc->hostName[cc->numServers], namep);
68         cc->numServers++;
69         return 0;
70 }
71
72 #else /* WIN32 */
73 #include <sys/param.h>
74 #include <netdb.h>
75 #include <arpa/inet.h>
76 #include <sys/socket.h>
77 #include <unistd.h>
78
79 #include <afs/param.h>
80 #include <afs/auth.h>
81 #include <afs/cellconfig.h>
82 #include <afs/vice.h>
83 #include <afs/venus.h>
84 #include <afs/ptserver.h>
85
86 #define krb_err_text(status) krb_err_txt[status]
87
88 /* Cheesy test for determining AFS 3.5. */
89 #ifndef AFSCONF_CLIENTNAME
90 #define AFS35
91 #endif
92
93 #ifdef AFS35
94 #include <afs/dirpath.h>
95 #else
96 #define AFSDIR_CLIENT_ETC_DIRPATH AFSCONF_CLIENTNAME
97 #endif
98
99 #endif /* WIN32 */
100
101 #include "linked_list.h"
102
103 #define AFSKEY "afs"
104 #define AFSINST ""
105
106 #define AKLOG_SUCCESS 0
107 #define AKLOG_USAGE 1
108 #define AKLOG_SOMETHINGSWRONG 2
109 #define AKLOG_AFS 3
110 #define AKLOG_KERBEROS 4
111 #define AKLOG_TOKEN 5
112 #define AKLOG_BADPATH 6
113 #define AKLOG_MISC 7
114
115 #ifndef NULL
116 #define NULL 0
117 #endif
118
119 #ifndef TRUE
120 #define TRUE 1
121 #endif
122
123 #ifndef FALSE
124 #define FALSE 0
125 #endif
126
127 #ifndef MAXSYMLINKS
128 #define MAXSYMLINKS 15
129 #endif
130
131 #define DIR '/'                 /* Character that divides directories */
132 #define DIRSTRING "/"           /* String form of above */
133 #define VOLMARKER ':'           /* Character separating cellname from mntpt */
134 #define VOLMARKERSTRING ":"     /* String form of above */
135
136 typedef struct {
137     char cell[BUFSIZ];
138     char realm[REALM_SZ];
139 } cellinfo_t;
140
141
142 struct afsconf_cell ak_cellconfig; /* General information about the cell */
143
144 static char *progname = NULL;   /* Name of this program */
145 static int dflag = FALSE;       /* Give debugging information */
146 static int noprdb = FALSE;      /* Skip resolving name to id? */
147 static int force = FALSE;       /* Bash identical tokens? */
148 static linked_list authedcells; /* List of cells already logged to */
149
150 static int usev5 = TRUE;   /* use kerberos 5? */
151 static int use524 = FALSE;  /* use krb524? */
152 static krb5_ccache _krb425_ccache;
153
154 long GetLocalCell(struct afsconf_dir **pconfigdir, char *local_cell)
155 {
156     if (!(*pconfigdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)))
157     {
158         fprintf(stderr, "%s: can't get afs configuration (afsconf_Open(%s))\n",
159                  progname, AFSDIR_CLIENT_ETC_DIRPATH);
160         exit(AKLOG_AFS);
161     }
162
163     return afsconf_GetLocalCell(*pconfigdir, local_cell, MAXCELLCHARS);
164 }
165
166 long GetCellInfo(struct afsconf_dir **pconfigdir, char* cell, 
167 struct afsconf_cell **pcellconfig)
168 {
169     return afsconf_GetCellInfo(*pconfigdir, cell, NULL, *pcellconfig);
170 }
171
172 void CloseConf(struct afsconf_dir **pconfigdir)
173 {       
174     (void) afsconf_Close(*pconfigdir);
175 }
176
177 #define ALLOW_REGISTER 1
178 void ViceIDToUsername(char *username, char *realm_of_user, char *realm_of_cell,
179                       char * cell_to_use, CREDENTIALS *c,
180                       int *status, 
181                       struct ktc_principal *aclient, struct ktc_principal *aserver, struct ktc_token *atoken)
182 {
183     static char lastcell[MAXCELLCHARS+1] = { 0 };
184     static char confname[512] = { 0 };
185     char username_copy[BUFSIZ];
186     long viceId;                        /* AFS uid of user */
187 #ifdef ALLOW_REGISTER
188     afs_int32 id;
189 #endif /* ALLOW_REGISTER */
190
191     if (confname[0] == '\0') {
192         strncpy(confname, AFSDIR_CLIENT_ETC_DIRPATH, sizeof(confname));
193         confname[sizeof(confname) - 2] = '\0';
194     }
195
196     if (dflag)
197         printf("About to resolve name %s to id\n", username);
198
199     /*
200     * Talk about DUMB!  It turns out that there is a bug in
201     * pr_Initialize -- even if you give a different cell name
202     * to it, it still uses a connection to a previous AFS server
203     * if one exists.  The way to fix this is to change the
204     * _filename_ argument to pr_Initialize - that forces it to
205     * re-initialize the connection.  We do this by adding and
206     * removing a "/" on the end of the configuration directory name.
207     */
208
209     if (lastcell[0] != '\0' && (strcmp(lastcell, aserver->cell) != 0)) {
210         int i = strlen(confname);
211         if (confname[i - 1] == '/') {
212             confname[i - 1] = '\0';
213         } else {
214             confname[i] = '/';
215             confname[i + 1] = '\0';
216         }
217     }
218
219     strcpy(lastcell, aserver->cell);
220
221     if (!pr_Initialize (0, confname, aserver->cell))
222         *status = pr_SNameToId (username, &viceId);
223
224     if (dflag)
225     {
226         if (*status)
227             printf("Error %d\n", *status);
228         else
229             printf("Id %d\n", viceId);
230     }       
231
232     /*
233      * This is a crock, but it is Transarc's crock, so
234      * we have to play along in order to get the
235      * functionality.  The way the afs id is stored is
236      * as a string in the username field of the token.
237      * Contrary to what you may think by looking at
238      * the code for tokens, this hack (AFS ID %d) will
239      * not work if you change %d to something else.
240      */
241
242     /*
243      * This code is taken from cklog -- it lets people
244      * automatically register with the ptserver in foreign cells
245      */
246
247 #ifdef ALLOW_REGISTER
248     if (*status == 0) {
249         if (viceId != ANONYMOUSID) {
250 #else /* ALLOW_REGISTER */
251             if ((*status == 0) && (viceId != ANONYMOUSID))
252 #endif /* ALLOW_REGISTER */
253             {
254 #ifdef AFS_ID_TO_NAME
255                 strncpy(username_copy, username, BUFSIZ);
256                 snprintf (username, BUFSIZ, "%s (AFS ID %d)", username_copy, (int) viceId);
257 #endif /* AFS_ID_TO_NAME */
258             }
259 #ifdef ALLOW_REGISTER
260         } else if (strcmp(realm_of_user, realm_of_cell) != 0) {
261             if (dflag) {
262                 printf("doing first-time registration of %s "
263                         "at %s\n", username, cell_to_use);
264             }
265             id = 0;
266             strncpy(aclient->name, username, MAXKTCNAMELEN - 1);
267             strcpy(aclient->instance, "");
268             strncpy(aclient->cell, c->realm, MAXKTCREALMLEN - 1);
269             if ((*status = ktc_SetToken(aserver, atoken, aclient, 0))) {
270                 printf("%s: unable to obtain tokens for cell %s "
271                         "(status: %d).\n", progname, cell_to_use, status);
272                 *status = AKLOG_TOKEN;
273                 return ;
274             }
275
276             /*
277              * In case you're wondering, we don't need to change the
278              * filename here because we're still connecting to the
279              * same cell -- we're just using a different authentication
280              * level
281              */
282
283             if ((*status = pr_Initialize(1L, confname, aserver->cell, 0))) {
284                 printf("Error %d\n", status);
285                 return;
286             }
287
288             if ((*status = pr_CreateUser(username, &id))) {
289                 printf("%s: unable to create remote PTS "
290                         "user %s in cell %s (status: %d).\n", progname,
291                         username, cell_to_use, *status);
292             } else {
293                 printf("created cross-cell entry for %s at %s\n",
294                         username, cell_to_use);
295 #ifdef AFS_ID_TO_NAME
296                 strncpy(username_copy, username, BUFSIZ);
297                 snprintf (username, BUFSIZ, "%s (AFS ID %d)", username_copy, (int) viceId);
298 #endif /* AFS_ID_TO_NAME */
299             }
300         }
301     }
302 #endif /* ALLOW_REGISTER */
303 }
304
305 char *LastComponent(char *str)
306 {
307     char *ret = strrchr(str, DIR);
308
309 #ifdef WIN32
310     if (!ret)
311         ret = strrchr(str, BDIR);
312 #endif
313     return ret;
314 }
315
316 int FirstComponent(char *str)
317 {
318     return (int)(
319 #ifdef WIN32
320                 strchr(str, BDIR) ||
321 #endif
322                 strchr(str, DIR));
323 }
324
325 void CopyPathColon(char *origpath, char *path, char *pathtocheck)
326 {
327 #ifdef WIN32
328     if (origpath[1] == DRIVECOLON)
329     {
330         strncpy(pathtocheck, origpath, 2);
331         strcpy(path, origpath+2);
332     }
333     else
334 #endif
335         strcpy(path, origpath);
336 }
337
338 int BeginsWithDir(char *str, int colon)
339 {
340     return (str[0] == DIR) ||
341 #ifdef WIN32
342         ((str[0] == BDIR) || (colon && str[1] == DRIVECOLON));
343 #else
344     FALSE;
345 #endif
346 }
347
348
349 /* This is a pretty gross hack.  Linking against the Transarc
350 * libraries pulls in some rxkad functions which use des.  (I don't
351 * think they ever get called.)  With Transarc-supplied libraries this
352 * creates a reliance on the symbol des_pcbc_init() which is only in
353 * Transarc's DES libraries (it's an exportability symbol-hiding
354 * thing), which we don't want to use because they don't work with
355 * MIT's krb4 routines.  So export a des_pcbc_init() symbol here so we
356 * don't have to link against Transarc's des library.
357 */
358 int des_pcbc_init()
359 {
360     abort();
361 }
362
363 static int get_cred(char *name, char *inst, char *realm, CREDENTIALS *c)
364 {
365     int status;
366
367     status = krb_get_cred(name, inst, realm, c);
368     if (status != KSUCCESS)
369     {
370 #ifdef DONT_HAVE_GET_AD_TKT
371         KTEXT_ST ticket;
372         status = krb_mk_req(&ticket, name, inst, realm, 0);
373 #else
374         status = get_ad_tkt(name, inst, realm, 255);
375 #endif
376         if (status == KSUCCESS)
377             status = krb_get_cred(name, inst, realm, c);
378     }       
379
380     return (status);
381 }
382
383 static int get_v5cred(krb5_context context, 
384                       char *name, char *inst, char *realm, CREDENTIALS *c,
385                       krb5_creds **creds)
386 {
387     krb5_creds increds;
388     krb5_error_code r;
389     static krb5_principal client_principal = 0;
390
391     memset((char *)&increds, 0, sizeof(increds));
392
393         if ((r = krb5_build_principal(context, &increds.server,
394                      strlen(realm), realm,
395                      name,
396            (inst && strlen(inst)) ? inst : 0,
397                      0))) {
398         return((int)r);
399     }
400
401     if (!_krb425_ccache)
402         krb5_cc_default(context, &_krb425_ccache);
403     if (!client_principal)
404         krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
405
406     increds.client = client_principal;
407     increds.times.endtime = 0;
408         /* Ask for DES since that is what V4 understands */
409     increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
410
411     r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
412     if (r)
413         return((int)r);
414
415     /* This requires krb524d to be running with the KDC */
416     if (c != NULL)
417         r = krb5_524_convert_creds(context, *creds, c);
418     return((int)r);
419 }
420
421 /* There is no header for this function.  It is supposed to be private */
422 int krb_get_admhst(char *h,char *r, int n);
423
424 static char *afs_realm_of_cell(struct afsconf_cell *cellconfig)
425 {
426         char krbhst[MAX_HSTNM];
427         static char krbrlm[REALM_SZ+1];
428
429         if (!cellconfig)
430                 return 0;
431
432         strcpy(krbrlm, (char *) krb_realmofhost(cellconfig->hostName[0]));
433
434         if (krb_get_admhst(krbhst, krbrlm, 1) != KSUCCESS)
435         {
436                 char *s = krbrlm;
437                 char *t = cellconfig->name;
438                 int c;
439
440                 while (c = *t++)
441                 {
442                         if (islower(c))
443                                 c = toupper(c);
444                         *s++ = c;
445                 }
446                 *s++ = 0;
447         }
448         return krbrlm;
449 }
450
451 static char *afs_realm_of_cell5(krb5_context context, struct afsconf_cell *cellconfig)
452 {
453         char ** krbrlms = 0;
454         static char krbrlm[REALM_SZ+1];
455         krb5_error_code status;
456
457         if (!cellconfig)
458                 return 0;
459
460         status = krb5_get_host_realm( context, cellconfig->hostName[0], &krbrlms );
461
462         if (status == 0 && krbrlms && krbrlms[0]) {
463                 strcpy(krbrlm, krbrlms[0]);
464     } else {
465                 strcpy(krbrlm, cellconfig->name);
466                 strupr(krbrlm);
467         }
468
469         if (krbrlms)
470                 krb5_free_host_realm( context, krbrlms );
471
472         return krbrlm;
473 }
474
475 static char *copy_cellinfo(cellinfo_t *cellinfo)
476 {
477         cellinfo_t *new_cellinfo;
478
479         if (new_cellinfo = (cellinfo_t *)malloc(sizeof(cellinfo_t)))
480                 memcpy(new_cellinfo, cellinfo, sizeof(cellinfo_t));
481
482         return ((char *)new_cellinfo);
483 }
484
485
486 static char *copy_string(char *string)
487 {
488         char *new_string;
489
490         if (new_string = (char *)calloc(strlen(string) + 1, sizeof(char)))
491                 (void) strcpy(new_string, string);
492
493         return (new_string);
494 }
495
496
497 static int get_cellconfig(char *cell, struct afsconf_cell *cellconfig,
498                                                   char *local_cell)
499 {
500         int status = AKLOG_SUCCESS;
501         struct afsconf_dir *configdir = 0;
502
503         memset(local_cell, 0, sizeof(local_cell));
504         memset(cellconfig, 0, sizeof(*cellconfig));
505
506         if (GetLocalCell(&configdir, local_cell))
507         {
508                 fprintf(stderr, "%s: can't determine local cell.\n", progname);
509                 exit(AKLOG_AFS);
510         }
511
512         if ((cell == NULL) || (cell[0] == 0))
513                 cell = local_cell;
514
515         if (GetCellInfo(&configdir, cell, &cellconfig))
516         {
517                 fprintf(stderr, "%s: Can't get information about cell %s.\n",
518                         progname, cell);
519                 status = AKLOG_AFS;
520         }
521
522
523         CloseConf(&configdir);
524
525         return(status);
526 }
527
528 static int get_v5_user_realm(krb5_context context,char *realm)
529 {
530     static krb5_principal client_principal = 0;
531     int i;
532
533     if (!_krb425_ccache)
534         krb5_cc_default(context, &_krb425_ccache);
535     if (!client_principal)
536         krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
537
538     i = krb5_princ_realm(context, client_principal)->length;
539     if (i < REALM_SZ-1) i = REALM_SZ-1;
540     strncpy(realm,krb5_princ_realm(context, client_principal)->data,i);
541     realm[i] = 0;
542     return(KSUCCESS);
543 }
544
545 /*
546 * Log to a cell.  If the cell has already been logged to, return without
547 * doing anything.  Otherwise, log to it and mark that it has been logged
548 * to.  */
549 static int auth_to_cell(krb5_context context, char *cell, char *realm)
550 {
551     int status = AKLOG_SUCCESS;
552     char username[BUFSIZ];      /* To hold client username structure */
553
554     char name[ANAME_SZ];                /* Name of afs key */
555     char instance[INST_SZ];     /* Instance of afs key */
556     char realm_of_user[REALM_SZ]; /* Kerberos realm of user */
557     char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
558     char local_cell[MAXCELLCHARS+1];
559     char cell_to_use[MAXCELLCHARS+1]; /* Cell to authenticate to */
560
561     krb5_creds *v5cred = NULL;
562     CREDENTIALS c;
563     struct ktc_principal aserver;
564     struct ktc_principal aclient;
565     struct ktc_token atoken, btoken;
566
567
568     /* try to avoid an expensive call to get_cellconfig */
569     if (cell && ll_string_check(&authedcells, cell))
570     {
571         if (dflag)
572             printf("Already authenticated to %s (or tried to)\n", cell);
573         return(AKLOG_SUCCESS);
574     }
575
576     memset(name, 0, sizeof(name));
577     memset(instance, 0, sizeof(instance));
578     memset(realm_of_user, 0, sizeof(realm_of_user));
579     memset(realm_of_cell, 0, sizeof(realm_of_cell));
580
581     /* NULL or empty cell returns information on local cell */
582     if (status = get_cellconfig(cell, &ak_cellconfig, local_cell))
583         return(status);
584
585     strncpy(cell_to_use, ak_cellconfig.name, MAXCELLCHARS);
586     cell_to_use[MAXCELLCHARS] = 0;
587
588     if (ll_string_check(&authedcells, cell_to_use))
589     {
590         if (dflag)
591             printf("Already authenticated to %s (or tried to)\n", cell_to_use);
592         return(AKLOG_SUCCESS);
593     }
594
595     /*
596      * Record that we have attempted to log to this cell.  We do this
597      * before we try rather than after so that we will not try
598      * and fail repeatedly for one cell.
599      */
600     (void)ll_add_string(&authedcells, cell_to_use);
601
602     if (dflag)
603         printf("Authenticating to cell %s.\n", cell_to_use);
604
605     if (realm && realm[0])
606         strcpy(realm_of_cell, realm);
607     else
608         strcpy(realm_of_cell,
609                 (usev5)? 
610                 afs_realm_of_cell5(context, &ak_cellconfig) : 
611                 afs_realm_of_cell(&ak_cellconfig));
612
613     /* We use the afs.<cellname> convention here... */
614     strcpy(name, AFSKEY);
615     strncpy(instance, cell_to_use, sizeof(instance));
616     instance[sizeof(instance)-1] = '\0';
617
618     /*
619      * Extract the session key from the ticket file and hand-frob an
620      * afs style authenticator.
621      */
622
623     if (usev5) 
624     { /* using krb5 */
625         int retry = 1;
626
627       try_v5:
628         if (dflag)
629             printf("Getting v5 tickets: %s/%s@%s\n", name, instance, realm_of_cell);
630         status = get_v5cred(context, name, instance, realm_of_cell, 
631                             use524 ? &c : NULL, &v5cred);
632         if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
633             if (dflag)
634                 printf("Getting v5 tickets: %s@%s\n", name, realm_of_cell);
635             status = get_v5cred(context, name, "", realm_of_cell, 
636                                 use524 ? &c : NULL, &v5cred);
637         }
638         if ( status == KRB5KRB_AP_ERR_MSG_TYPE && retry ) {
639             retry = 0;
640             goto try_v5;
641         }       
642     }       
643     else 
644     {
645         /*
646          * Try to obtain AFS tickets.  Because there are two valid service
647          * names, we will try both, but trying the more specific first.
648          *
649          *      afs.<cell>@<realm>
650          *      afs@<realm>
651          */
652         if (dflag)
653             printf("Getting tickets: %s.%s@%s\n", name, instance, realm_of_cell);
654         status = get_cred(name, instance, realm_of_cell, &c);
655         if (status == KDC_PR_UNKNOWN)
656         {
657             if (dflag)
658                 printf("Getting tickets: %s@%s\n", name, realm_of_cell);
659             status = get_cred(name, "", realm_of_cell, &c);
660         }
661     } 
662
663     /* TODO: get k5 error text */
664     if (status != KSUCCESS)
665     {
666         if (dflag)
667             printf("Kerberos error code returned by get_cred: %d\n", status);
668         fprintf(stderr, "%s: Couldn't get %s AFS tickets: %s\n",
669                  progname, cell_to_use, 
670                  (usev5)?"":
671                  krb_err_text(status));
672         return(AKLOG_KERBEROS);
673     }
674
675     strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
676     strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
677     strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1);
678
679     if (usev5 && !use524) {
680         /* This code inserts the entire K5 ticket into the token
681          * No need to perform a krb524 translation which is
682          * commented out in the code below
683          */
684         char * p;
685         int len;
686         
687         len = min(v5cred->client->data[0].length,MAXKTCNAMELEN - 1);
688         strncpy(username, v5cred->client->data[0].data, len);
689         username[len] = '\0';
690
691         if ( v5cred->client->length > 1 ) {
692             strcat(username, ".");
693             p = username + strlen(username);
694             len = min(v5cred->client->data[1].length,MAXKTCNAMELEN - strlen(username) - 1);
695             strncpy(p, v5cred->client->data[1].data, len);
696             p[len] = '\0';
697         }
698
699         memset(&atoken, '\0', sizeof(atoken));
700         atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
701         atoken.startTime = v5cred->times.starttime;
702         atoken.endTime = v5cred->times.endtime;
703         memcpy(&atoken.sessionKey, v5cred->keyblock.contents, v5cred->keyblock.length);
704         atoken.ticketLen = v5cred->ticket.length;
705         memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen);
706     } else {
707         strcpy (username, c.pname);
708         if (c.pinst[0])
709         {
710             strcat(username, ".");
711             strcat(username, c.pinst);
712         }
713
714         atoken.kvno = c.kvno;
715         atoken.startTime = c.issue_date;
716         /* ticket lifetime is in five-minutes blocks. */
717         atoken.endTime = c.issue_date + ((unsigned char)c.lifetime * 5 * 60);
718
719         memcpy(&atoken.sessionKey, c.session, 8);
720         atoken.ticketLen = c.ticket_st.length;
721         memcpy(atoken.ticket, c.ticket_st.dat, atoken.ticketLen);
722     }
723
724     if (!force &&
725          !ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient) &&
726          atoken.kvno == btoken.kvno &&
727          atoken.ticketLen == btoken.ticketLen &&
728          !memcmp(&atoken.sessionKey, &btoken.sessionKey, sizeof(atoken.sessionKey)) &&
729          !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen))
730     {       
731         if (dflag)
732             printf("Identical tokens already exist; skipping.\n");
733         return 0;
734     }
735
736     if (noprdb)
737     {
738         if (dflag)
739             printf("Not resolving name %s to id (-noprdb set)\n", username);
740     }       
741     else    
742     {
743         if (usev5) {
744             if((status = get_v5_user_realm(context, realm_of_user)) != KSUCCESS) {
745                 fprintf(stderr, "%s: Couldn't determine realm of user: %d\n",
746                          progname, status);
747                 return(AKLOG_KERBEROS);
748             }
749         } else {
750             if ((status = krb_get_tf_realm(TKT_FILE, realm_of_user)) != KSUCCESS)
751             {
752                 fprintf(stderr, "%s: Couldn't determine realm of user: %s)",
753                          progname, krb_err_text(status));
754                 return(AKLOG_KERBEROS);
755             }
756         }
757
758         if (strcmp(realm_of_user, realm_of_cell))
759         {
760             strcat(username, "@");
761             strcat(username, realm_of_user);
762         }
763
764         ViceIDToUsername(username, realm_of_user, realm_of_cell, cell_to_use, &c, &status, &aclient, &aserver, &atoken);
765     }
766
767     if (dflag)
768         printf("Set username to %s\n", username);
769
770     /* Reset the "aclient" structure before we call ktc_SetToken.
771      * This structure was first set by the ktc_GetToken call when
772      * we were comparing whether identical tokens already existed.
773      */
774     strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
775     strcpy(aclient.instance, "");
776     
777     if (usev5 && !use524) {
778         int len = min(v5cred->client->realm.length,MAXKTCNAMELEN - 1);
779         strncpy(aclient.cell, v5cred->client->realm.data, len);
780         aclient.cell[len] = '\0';
781     } else
782         strncpy(aclient.cell, c.realm, MAXKTCREALMLEN - 1);
783
784     if (dflag)
785         printf("Getting tokens.\n");
786     if (status = ktc_SetToken(&aserver, &atoken, &aclient, 0))
787     {
788         fprintf(stderr,
789                  "%s: unable to obtain tokens for cell %s (status: %d).\n",
790                  progname, cell_to_use, status);
791         status = AKLOG_TOKEN;
792     }
793
794     return(status);
795 }
796
797 static int get_afs_mountpoint(char *file, char *mountpoint, int size)
798 {
799     char our_file[MAXPATHLEN + 1];
800     char *parent_dir;
801     char *last_component;
802     struct ViceIoctl vio;
803     char cellname[BUFSIZ];
804
805     memset(our_file, 0, sizeof(our_file));
806     strcpy(our_file, file);
807
808     if (last_component = LastComponent(our_file))
809     {
810         *last_component++ = 0;
811         parent_dir = our_file;
812     }
813     else
814     {
815         last_component = our_file;
816         parent_dir = ".";
817     }
818
819     memset(cellname, 0, sizeof(cellname));
820
821     vio.in = last_component;
822     vio.in_size = strlen(last_component)+1;
823     vio.out_size = size;
824     vio.out = mountpoint;
825
826     if (!pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, &vio, 0))
827     {
828         if (strchr(mountpoint, VOLMARKER) == NULL)
829         {
830             vio.in = file;
831             vio.in_size = strlen(file) + 1;
832             vio.out_size = sizeof(cellname);
833             vio.out = cellname;
834
835             if (!pioctl(file, VIOC_FILE_CELL_NAME, &vio, 1))
836             {
837                 strcat(cellname, VOLMARKERSTRING);
838                 strcat(cellname, mountpoint + 1);
839                 memset(mountpoint + 1, 0, size - 1);
840                 strcpy(mountpoint + 1, cellname);
841             }
842         }
843         return(TRUE);
844     }
845     else {
846         return(FALSE);
847     }
848 }       
849
850 /*
851 * This routine each time it is called returns the next directory
852 * down a pathname.  It resolves all symbolic links.  The first time
853 * it is called, it should be called with the name of the path
854 * to be descended.  After that, it should be called with the arguemnt
855 * NULL.
856 */
857 static char *next_path(char *origpath)
858 {
859     static char path[MAXPATHLEN + 1];
860     static char pathtocheck[MAXPATHLEN + 1];
861
862     int link = FALSE;           /* Is this a symbolic link? */
863     char linkbuf[MAXPATHLEN + 1];
864     char tmpbuf[MAXPATHLEN + 1];
865
866     static char *last_comp;     /* last component of directory name */
867     static char *elast_comp;    /* End of last component */
868     char *t;
869     int len;
870
871     static int symlinkcount = 0;        /* We can't exceed MAXSYMLINKS */
872
873     /* If we are given something for origpath, we are initializing only. */
874     if (origpath)
875     {
876         memset(path, 0, sizeof(path));
877         memset(pathtocheck, 0, sizeof(pathtocheck));
878         CopyPathColon(origpath, path, pathtocheck);
879         last_comp = path;
880         symlinkcount = 0;
881         return(NULL);
882     }
883
884     /* We were not given origpath; find then next path to check */
885
886     /* If we've gotten all the way through already, return NULL */
887     if (last_comp == NULL)
888         return(NULL);
889
890     do
891     {
892         while (BeginsWithDir(last_comp, FALSE))
893             strncat(pathtocheck, last_comp++, 1);
894         len = (elast_comp = LastComponent(last_comp))
895             ? elast_comp - last_comp : strlen(last_comp);
896         strncat(pathtocheck, last_comp, len);
897         memset(linkbuf, 0, sizeof(linkbuf));
898         if (link = (readlink(pathtocheck, linkbuf, sizeof(linkbuf)) > 0))
899         {
900             if (++symlinkcount > MAXSYMLINKS)
901             {
902                 fprintf(stderr, "%s: %s\n", progname, strerror(ELOOP));
903                 exit(AKLOG_BADPATH);
904             }
905             memset(tmpbuf, 0, sizeof(tmpbuf));
906             if (elast_comp)
907                 strcpy(tmpbuf, elast_comp);
908             if (BeginsWithDir(linkbuf, FALSE))
909             {
910                 /*
911                 * If this is a symbolic link to an absolute path,
912                 * replace what we have by the absolute path.
913                 */
914                 memset(path, 0, strlen(path));
915                 memcpy(path, linkbuf, sizeof(linkbuf));
916                 strcat(path, tmpbuf);
917                 last_comp = path;
918                 elast_comp = NULL;
919                 memset(pathtocheck, 0, sizeof(pathtocheck));
920             }
921             else
922             {
923                 /*
924                 * If this is a symbolic link to a relative path,
925                 * replace only the last component with the link name.
926                 */
927                 strncpy(last_comp, linkbuf, strlen(linkbuf) + 1);
928                 strcat(path, tmpbuf);
929                 elast_comp = NULL;
930                 if (t = LastComponent(pathtocheck))
931                 {
932                     t++;
933                     memset(t, 0, strlen(t));
934                 }
935                 else
936                     memset(pathtocheck, 0, sizeof(pathtocheck));
937             }
938         }
939         else
940             last_comp = elast_comp;
941     }       
942     while(link);
943
944     return(pathtocheck);
945 }
946
947 /*
948 * This routine descends through a path to a directory, logging to
949 * every cell it encounters along the way.
950 */
951 static int auth_to_path(krb5_context context, char *path)
952 {
953     int status = AKLOG_SUCCESS;
954     int auth_to_cell_status = AKLOG_SUCCESS;
955
956     char *nextpath;
957     char pathtocheck[MAXPATHLEN + 1];
958     char mountpoint[MAXPATHLEN + 1];
959
960     char *cell;
961     char *endofcell;
962
963     /* Initialize */
964     if (BeginsWithDir(path, TRUE))
965         strcpy(pathtocheck, path);
966     else
967     {
968         if (getcwd(pathtocheck, sizeof(pathtocheck)) == NULL)
969         {
970             fprintf(stderr, "Unable to find current working directory:\n");
971             fprintf(stderr, "%s\n", pathtocheck);
972             fprintf(stderr, "Try an absolute pathname.\n");
973             exit(AKLOG_BADPATH);
974         }
975         else
976         {
977             /* in WIN32, if getcwd returns a root dir (eg: c:\), the returned string
978             * will already have a trailing slash ('\'). Otherwise, the string will
979             * end in the last directory name */
980 #ifdef WIN32    
981             if(pathtocheck[strlen(pathtocheck) - 1] != BDIR)
982 #endif  
983                 strcat(pathtocheck, DIRSTRING);
984             strcat(pathtocheck, path);
985         }
986     }
987     next_path(pathtocheck);
988
989     /* Go on to the next level down the path */
990     while (nextpath = next_path(NULL))
991     {
992         strcpy(pathtocheck, nextpath);
993         if (dflag)
994             printf("Checking directory [%s]\n", pathtocheck);
995         /*
996         * If this is an afs mountpoint, determine what cell from
997         * the mountpoint name which is of the form
998         * #cellname:volumename or %cellname:volumename.
999         */
1000         if (get_afs_mountpoint(pathtocheck, mountpoint, sizeof(mountpoint)))
1001         {
1002             if(dflag)
1003                 printf("Found mount point [%s]\n", mountpoint);
1004             /* skip over the '#' or '%' */
1005             cell = mountpoint + 1;
1006             if (endofcell = strchr(mountpoint, VOLMARKER))
1007             {
1008                 *endofcell = '\0';
1009                 if (auth_to_cell_status = auth_to_cell(context, cell, NULL))
1010                 {
1011                     if (status == AKLOG_SUCCESS)
1012                         status = auth_to_cell_status;
1013                     else if (status != auth_to_cell_status)
1014                         status = AKLOG_SOMETHINGSWRONG;
1015                 }
1016             }
1017         }
1018         else
1019         {
1020             struct stat st;
1021
1022             if (lstat(pathtocheck, &st) < 0)
1023             {
1024                 /*
1025                 * If we've logged and still can't stat, there's
1026                 * a problem...
1027                 */
1028                 fprintf(stderr, "%s: stat(%s): %s\n", progname,
1029                          pathtocheck, strerror(errno));
1030                 return(AKLOG_BADPATH);
1031             }
1032             else if (!S_ISDIR(st.st_mode))
1033             {
1034                 /* Allow only directories */
1035                 fprintf(stderr, "%s: %s: %s\n", progname, pathtocheck,
1036                          strerror(ENOTDIR));
1037                 return(AKLOG_BADPATH);
1038             }
1039         }
1040     }
1041
1042     return(status);
1043 }
1044
1045 /* Print usage message and exit */
1046 static void usage(void)
1047 {
1048     fprintf(stderr, "\nUsage: %s %s%s%s%s\n", progname,
1049              "[-d] [[-cell | -c] cell [-k krb_realm]] ",
1050              "[[-p | -path] pathname]\n",
1051              "    [-noprdb] [-force]\n",
1052              "    [-5 [-m]| -4]\n"
1053              );
1054     fprintf(stderr, "    -d gives debugging information.\n");
1055     fprintf(stderr, "    krb_realm is the kerberos realm of a cell.\n");
1056     fprintf(stderr, "    pathname is the name of a directory to which ");
1057     fprintf(stderr, "you wish to authenticate.\n");
1058     fprintf(stderr, "    -noprdb means don't try to determine AFS ID.\n");
1059     fprintf(stderr, "    -5 or -4 selects whether to use Kerberos V or Kerberos IV.\n"
1060                     "       (default is Kerberos V)\n");
1061     fprintf(stderr, "       -m means use krb524d to convert Kerberos V tickets.\n");
1062     fprintf(stderr, "    No commandline arguments means ");
1063     fprintf(stderr, "authenticate to the local cell.\n");
1064     fprintf(stderr, "\n");
1065     exit(AKLOG_USAGE);
1066 }
1067
1068 int main(int argc, char *argv[])
1069 {
1070     int status = AKLOG_SUCCESS;
1071     int i;
1072     int somethingswrong = FALSE;
1073
1074     cellinfo_t cellinfo;
1075
1076     extern char *progname;      /* Name of this program */
1077
1078     extern int dflag;           /* Debug mode */
1079
1080     int cmode = FALSE;          /* Cellname mode */
1081     int pmode = FALSE;          /* Path name mode */
1082
1083     char realm[REALM_SZ];               /* Kerberos realm of afs server */
1084     char cell[BUFSIZ];          /* Cell to which we are authenticating */
1085     char path[MAXPATHLEN + 1];  /* Path length for path mode */
1086
1087     linked_list cells;          /* List of cells to log to */
1088     linked_list paths;          /* List of paths to log to */
1089     ll_node *cur_node;
1090
1091     krb5_context context = 0;
1092
1093     memset(&cellinfo, 0, sizeof(cellinfo));
1094
1095     memset(realm, 0, sizeof(realm));
1096     memset(cell, 0, sizeof(cell));
1097     memset(path, 0, sizeof(path));
1098
1099     ll_init(&cells);
1100     ll_init(&paths);
1101
1102     /* Store the program name here for error messages */
1103     if (progname = LastComponent(argv[0]))
1104         progname++;
1105     else
1106         progname = argv[0];
1107
1108     /* Initialize list of cells to which we have authenticated */
1109     (void)ll_init(&authedcells);
1110
1111     /* Parse commandline arguments and make list of what to do. */
1112     for (i = 1; i < argc; i++)
1113     {
1114         if (strcmp(argv[i], "-d") == 0)
1115             dflag++;
1116         else if (strcmp(argv[i], "-5") == 0)
1117             usev5++;
1118         else if (strcmp(argv[i], "-m") == 0)
1119             use524++;
1120         else if (strcmp(argv[i], "-4") == 0)
1121             usev5 = 0;
1122         else if (strcmp(argv[i], "-noprdb") == 0)
1123             noprdb++;
1124         else if (strcmp(argv[i], "-force") == 0)
1125             force++;
1126         else if (((strcmp(argv[i], "-cell") == 0) ||
1127                    (strcmp(argv[i], "-c") == 0)) && !pmode)
1128         {       
1129             if (++i < argc)
1130             {
1131                 cmode++;
1132                 strcpy(cell, argv[i]);
1133             }
1134             else
1135                 usage();
1136         }
1137         else if (((strcmp(argv[i], "-path") == 0) ||
1138                    (strcmp(argv[i], "-p") == 0)) && !cmode)
1139         {       
1140             if (++i < argc)
1141             {
1142                 pmode++;
1143                 strcpy(path, argv[i]);
1144             }
1145             else
1146                 usage();
1147         }
1148         else if (argv[i][0] == '-')
1149             usage();
1150         else if (!pmode && !cmode)
1151         {
1152             if (FirstComponent(argv[i]) || (strcmp(argv[i], ".") == 0) ||
1153                  (strcmp(argv[i], "..") == 0))
1154             {
1155                 pmode++;
1156                 strcpy(path, argv[i]);
1157             }
1158             else
1159             {
1160                 cmode++;
1161                 strcpy(cell, argv[i]);
1162             }
1163         }
1164         else
1165             usage();
1166
1167         if (cmode)
1168         {
1169             if (((i + 1) < argc) && (strcmp(argv[i + 1], "-k") == 0))
1170             {
1171                 i += 2;
1172                 if (i < argc)
1173                     strcpy(realm, argv[i]);
1174                 else
1175                     usage();
1176             }
1177             /* Add this cell to list of cells */
1178             strcpy(cellinfo.cell, cell);
1179             strcpy(cellinfo.realm, realm);
1180             if (cur_node = ll_add_node(&cells, ll_tail))
1181             {
1182                 char *new_cellinfo;
1183                 if (new_cellinfo = copy_cellinfo(&cellinfo))
1184                     ll_add_data(cur_node, new_cellinfo);
1185                 else
1186                 {
1187                     fprintf(stderr, "%s: failure copying cellinfo.\n", progname);
1188                     exit(AKLOG_MISC);
1189                 }
1190             }
1191             else
1192             {
1193                 fprintf(stderr, "%s: failure adding cell to cells list.\n",
1194                          progname);
1195                 exit(AKLOG_MISC);
1196             }
1197             memset(&cellinfo, 0, sizeof(cellinfo));
1198             cmode = FALSE;
1199             memset(cell, 0, sizeof(cell));
1200             memset(realm, 0, sizeof(realm));
1201         }
1202         else if (pmode)
1203         {
1204             /* Add this path to list of paths */
1205             if (cur_node = ll_add_node(&paths, ll_tail))
1206             {
1207                 char *new_path;
1208                 if (new_path = copy_string(path))
1209                     ll_add_data(cur_node, new_path);
1210                 else
1211                 {
1212                     fprintf(stderr, "%s: failure copying path name.\n",
1213                              progname);
1214                     exit(AKLOG_MISC);
1215                 }
1216             }
1217             else
1218             {
1219                 fprintf(stderr, "%s: failure adding path to paths list.\n",
1220                          progname);
1221                 exit(AKLOG_MISC);
1222             }
1223             pmode = FALSE;
1224             memset(path, 0, sizeof(path));
1225         }
1226     }
1227
1228     if(usev5)
1229         krb5_init_context(&context);
1230
1231     /* If nothing was given, log to the local cell. */
1232     if ((cells.nelements + paths.nelements) == 0)
1233         status = auth_to_cell(context, NULL, NULL);
1234     else
1235     {
1236         /* Log to all cells in the cells list first */
1237         for (cur_node = cells.first; cur_node; cur_node = cur_node->next)
1238         {
1239             memcpy(&cellinfo, cur_node->data, sizeof(cellinfo));
1240             if (status = auth_to_cell(context, 
1241                                        cellinfo.cell, cellinfo.realm))
1242                 somethingswrong++;
1243         }       
1244
1245         /* Then, log to all paths in the paths list */
1246         for (cur_node = paths.first; cur_node; cur_node = cur_node->next)
1247         {
1248             if (status = auth_to_path(context, 
1249                                        cur_node->data))
1250                 somethingswrong++;
1251         }
1252
1253         /*
1254         * If only one thing was logged to, we'll return the status
1255         * of the single call.  Otherwise, we'll return a generic
1256         * something failed status.
1257         */
1258         if (somethingswrong && ((cells.nelements + paths.nelements) > 1))
1259             status = AKLOG_SOMETHINGSWRONG;
1260     }       
1261
1262     if(usev5)
1263         krb5_free_context(context);
1264
1265     exit(status);
1266 }