objdir-20050622
[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 #include <afs/ptuser.h>
28
29 #ifdef WIN32
30 #include <windows.h>
31
32 #include <cm_config.h>
33 #include <auth.h>
34 #include <cellconfig.h>
35 #include <pioctl_nt.h>
36 #include <smb_iocons.h>
37
38 #define stat _stat
39 #define lstat stat
40 #define __S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
41 #define S_ISDIR(mode)          __S_ISTYPE((mode), _S_IFDIR)
42
43 #define DONT_HAVE_GET_AD_TKT
44 #define MAXSYMLINKS 255
45
46 /* Win32 uses get_krb_err_txt_entry(status) instead of krb_err_txt[status],
47 * so we use a bit of indirection like the GNU CVS sources.
48 */
49 #define krb_err_text(status) get_krb_err_txt_entry(status)
50
51 #define DRIVECOLON ':'          /* Drive letter separator */
52 #define BDIR '\\'               /* Other character that divides directories */
53
54 static int 
55 readlink(char *path, char *buf, int buffers)
56 {
57         return -1;
58 }
59
60 char * getcwd(char*, size_t);
61
62 static long 
63 get_cellconfig_callback(void *cellconfig, struct sockaddr_in *addrp, char *namep)
64 {
65         struct afsconf_cell *cc = (struct afsconf_cell *) cellconfig;
66
67         cc->hostAddr[cc->numServers] = *addrp;
68         strcpy(cc->hostName[cc->numServers], namep);
69         cc->numServers++;
70         return 0;
71 }
72
73 #else /* WIN32 */
74 #include <sys/param.h>
75 #include <netdb.h>
76 #include <arpa/inet.h>
77 #include <sys/socket.h>
78 #include <unistd.h>
79
80 #include <afs/param.h>
81 #include <afs/auth.h>
82 #include <afs/cellconfig.h>
83 #include <afs/vice.h>
84 #include <afs/venus.h>
85 #include <afs/ptserver.h>
86
87 #define krb_err_text(status) krb_err_txt[status]
88
89 /* Cheesy test for determining AFS 3.5. */
90 #ifndef AFSCONF_CLIENTNAME
91 #define AFS35
92 #endif
93
94 #ifdef AFS35
95 #include <afs/dirpath.h>
96 #else
97 #define AFSDIR_CLIENT_ETC_DIRPATH AFSCONF_CLIENTNAME
98 #endif
99
100 #endif /* WIN32 */
101
102 #include "linked_list.h"
103
104 #define AFSKEY "afs"
105 #define AFSINST ""
106
107 #define AKLOG_SUCCESS 0
108 #define AKLOG_USAGE 1
109 #define AKLOG_SOMETHINGSWRONG 2
110 #define AKLOG_AFS 3
111 #define AKLOG_KERBEROS 4
112 #define AKLOG_TOKEN 5
113 #define AKLOG_BADPATH 6
114 #define AKLOG_MISC 7
115
116 #ifndef NULL
117 #define NULL 0
118 #endif
119
120 #ifndef TRUE
121 #define TRUE 1
122 #endif
123
124 #ifndef FALSE
125 #define FALSE 0
126 #endif
127
128 #ifndef MAXSYMLINKS
129 #define MAXSYMLINKS 15
130 #endif
131
132 #define DIR '/'                 /* Character that divides directories */
133 #define DIRSTRING "/"           /* String form of above */
134 #define VOLMARKER ':'           /* Character separating cellname from mntpt */
135 #define VOLMARKERSTRING ":"     /* String form of above */
136
137 typedef struct {
138     char cell[BUFSIZ];
139     char realm[REALM_SZ];
140 } cellinfo_t;
141
142
143 struct afsconf_cell ak_cellconfig; /* General information about the cell */
144
145 static char *progname = NULL;   /* Name of this program */
146 static int dflag = FALSE;       /* Give debugging information */
147 static int noprdb = FALSE;      /* Skip resolving name to id? */
148 static int force = FALSE;       /* Bash identical tokens? */
149 static linked_list authedcells; /* List of cells already logged to */
150
151 static int usev5 = TRUE;   /* use kerberos 5? */
152 static int use524 = FALSE;  /* use krb524? */
153 static krb5_ccache _krb425_ccache;
154
155 long GetLocalCell(struct afsconf_dir **pconfigdir, char *local_cell)
156 {
157     if (!(*pconfigdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)))
158     {
159         fprintf(stderr, "%s: can't get afs configuration (afsconf_Open(%s))\n",
160                  progname, AFSDIR_CLIENT_ETC_DIRPATH);
161         exit(AKLOG_AFS);
162     }
163
164     return afsconf_GetLocalCell(*pconfigdir, local_cell, MAXCELLCHARS);
165 }
166
167 long GetCellInfo(struct afsconf_dir **pconfigdir, char* cell, 
168 struct afsconf_cell **pcellconfig)
169 {
170     return afsconf_GetCellInfo(*pconfigdir, cell, NULL, *pcellconfig);
171 }
172
173 void CloseConf(struct afsconf_dir **pconfigdir)
174 {       
175     (void) afsconf_Close(*pconfigdir);
176 }
177
178 #define ALLOW_REGISTER 1
179 void ViceIDToUsername(char *username, char *realm_of_user, char *realm_of_cell,
180                       char * cell_to_use, CREDENTIALS *c,
181                       int *status, 
182                       struct ktc_principal *aclient, struct ktc_principal *aserver, struct ktc_token *atoken)
183 {
184     static char lastcell[MAXCELLCHARS+1] = { 0 };
185     static char confname[512] = { 0 };
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))) {
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         if ( strchr(name,'.') != NULL ) {
628             fprintf(stderr, "%s: Can't support principal names including a dot.\n",
629                     progname);
630             return(AKLOG_MISC);
631         }
632
633       try_v5:
634         if (dflag)
635             printf("Getting v5 tickets: %s/%s@%s\n", name, instance, realm_of_cell);
636         status = get_v5cred(context, name, instance, realm_of_cell, 
637                             use524 ? &c : NULL, &v5cred);
638         if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
639             if (dflag)
640                 printf("Getting v5 tickets: %s@%s\n", name, realm_of_cell);
641             status = get_v5cred(context, name, "", realm_of_cell, 
642                                 use524 ? &c : NULL, &v5cred);
643         }
644         if ( status == KRB5KRB_AP_ERR_MSG_TYPE && retry ) {
645             retry = 0;
646             goto try_v5;
647         }       
648     }       
649     else 
650     {
651         /*
652          * Try to obtain AFS tickets.  Because there are two valid service
653          * names, we will try both, but trying the more specific first.
654          *
655          *      afs.<cell>@<realm>
656          *      afs@<realm>
657          */
658         if (dflag)
659             printf("Getting tickets: %s.%s@%s\n", name, instance, realm_of_cell);
660         status = get_cred(name, instance, realm_of_cell, &c);
661         if (status == KDC_PR_UNKNOWN)
662         {
663             if (dflag)
664                 printf("Getting tickets: %s@%s\n", name, realm_of_cell);
665             status = get_cred(name, "", realm_of_cell, &c);
666         }
667     } 
668
669     /* TODO: get k5 error text */
670     if (status != KSUCCESS)
671     {
672         if (dflag)
673             printf("Kerberos error code returned by get_cred: %d\n", status);
674         fprintf(stderr, "%s: Couldn't get %s AFS tickets: %s\n",
675                  progname, cell_to_use, 
676                  (usev5)?"":
677                  krb_err_text(status));
678         return(AKLOG_KERBEROS);
679     }
680
681     strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
682     strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
683     strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1);
684
685     if (usev5 && !use524) {
686         /* This code inserts the entire K5 ticket into the token
687          * No need to perform a krb524 translation which is
688          * commented out in the code below
689          */
690         char * p;
691         int len;
692         
693         len = min(v5cred->client->data[0].length,MAXKTCNAMELEN - 1);
694         strncpy(username, v5cred->client->data[0].data, len);
695         username[len] = '\0';
696
697         if ( v5cred->client->length > 1 ) {
698             strcat(username, ".");
699             p = username + strlen(username);
700             len = min(v5cred->client->data[1].length,MAXKTCNAMELEN - strlen(username) - 1);
701             strncpy(p, v5cred->client->data[1].data, len);
702             p[len] = '\0';
703         }
704
705         memset(&atoken, '\0', sizeof(atoken));
706         atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
707         atoken.startTime = v5cred->times.starttime;
708         atoken.endTime = v5cred->times.endtime;
709         memcpy(&atoken.sessionKey, v5cred->keyblock.contents, v5cred->keyblock.length);
710         atoken.ticketLen = v5cred->ticket.length;
711         memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen);
712     } else {
713         strcpy (username, c.pname);
714         if (c.pinst[0])
715         {
716             strcat(username, ".");
717             strcat(username, c.pinst);
718         }
719
720         atoken.kvno = c.kvno;
721         atoken.startTime = c.issue_date;
722         /* ticket lifetime is in five-minutes blocks. */
723         atoken.endTime = c.issue_date + ((unsigned char)c.lifetime * 5 * 60);
724
725         memcpy(&atoken.sessionKey, c.session, 8);
726         atoken.ticketLen = c.ticket_st.length;
727         memcpy(atoken.ticket, c.ticket_st.dat, atoken.ticketLen);
728     }
729
730     if (!force &&
731          !ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient) &&
732          atoken.kvno == btoken.kvno &&
733          atoken.ticketLen == btoken.ticketLen &&
734          !memcmp(&atoken.sessionKey, &btoken.sessionKey, sizeof(atoken.sessionKey)) &&
735          !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen))
736     {       
737         if (dflag)
738             printf("Identical tokens already exist; skipping.\n");
739         return 0;
740     }
741
742     if (noprdb)
743     {
744         if (dflag)
745             printf("Not resolving name %s to id (-noprdb set)\n", username);
746     }       
747     else    
748     {
749         if (usev5) {
750             if((status = get_v5_user_realm(context, realm_of_user)) != KSUCCESS) {
751                 fprintf(stderr, "%s: Couldn't determine realm of user: %d\n",
752                          progname, status);
753                 return(AKLOG_KERBEROS);
754             }
755         } else {
756             if ((status = krb_get_tf_realm(TKT_FILE, realm_of_user)) != KSUCCESS)
757             {
758                 fprintf(stderr, "%s: Couldn't determine realm of user: %s)",
759                          progname, krb_err_text(status));
760                 return(AKLOG_KERBEROS);
761             }
762         }
763
764         if (strcmp(realm_of_user, realm_of_cell))
765         {
766             strcat(username, "@");
767             strcat(username, realm_of_user);
768         }
769
770         ViceIDToUsername(username, realm_of_user, realm_of_cell, cell_to_use, &c, &status, &aclient, &aserver, &atoken);
771     }
772
773     if (dflag)
774         printf("Set username to %s\n", username);
775
776     /* Reset the "aclient" structure before we call ktc_SetToken.
777      * This structure was first set by the ktc_GetToken call when
778      * we were comparing whether identical tokens already existed.
779      */
780     strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
781     strcpy(aclient.instance, "");
782     
783     if (usev5 && !use524) {
784         int len = min(v5cred->client->realm.length,MAXKTCNAMELEN - 1);
785         strncpy(aclient.cell, v5cred->client->realm.data, len);
786         aclient.cell[len] = '\0';
787     } else
788         strncpy(aclient.cell, c.realm, MAXKTCREALMLEN - 1);
789
790     if (dflag)
791         printf("Getting tokens.\n");
792     if (status = ktc_SetToken(&aserver, &atoken, &aclient, 0))
793     {
794         fprintf(stderr,
795                  "%s: unable to obtain tokens for cell %s (status: %d).\n",
796                  progname, cell_to_use, status);
797         status = AKLOG_TOKEN;
798     }
799
800     return(status);
801 }
802
803 static int get_afs_mountpoint(char *file, char *mountpoint, int size)
804 {
805     char our_file[MAXPATHLEN + 1];
806     char *parent_dir;
807     char *last_component;
808     struct ViceIoctl vio;
809     char cellname[BUFSIZ];
810
811     memset(our_file, 0, sizeof(our_file));
812     strcpy(our_file, file);
813
814     if (last_component = LastComponent(our_file))
815     {
816         *last_component++ = 0;
817         parent_dir = our_file;
818     }
819     else
820     {
821         last_component = our_file;
822         parent_dir = ".";
823     }
824
825     memset(cellname, 0, sizeof(cellname));
826
827     vio.in = last_component;
828     vio.in_size = strlen(last_component)+1;
829     vio.out_size = size;
830     vio.out = mountpoint;
831
832     if (!pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, &vio, 0))
833     {
834         if (strchr(mountpoint, VOLMARKER) == NULL)
835         {
836             vio.in = file;
837             vio.in_size = strlen(file) + 1;
838             vio.out_size = sizeof(cellname);
839             vio.out = cellname;
840
841             if (!pioctl(file, VIOC_FILE_CELL_NAME, &vio, 1))
842             {
843                 strcat(cellname, VOLMARKERSTRING);
844                 strcat(cellname, mountpoint + 1);
845                 memset(mountpoint + 1, 0, size - 1);
846                 strcpy(mountpoint + 1, cellname);
847             }
848         }
849         return(TRUE);
850     }
851     else {
852         return(FALSE);
853     }
854 }       
855
856 /*
857 * This routine each time it is called returns the next directory
858 * down a pathname.  It resolves all symbolic links.  The first time
859 * it is called, it should be called with the name of the path
860 * to be descended.  After that, it should be called with the arguemnt
861 * NULL.
862 */
863 static char *next_path(char *origpath)
864 {
865     static char path[MAXPATHLEN + 1];
866     static char pathtocheck[MAXPATHLEN + 1];
867
868     int link = FALSE;           /* Is this a symbolic link? */
869     char linkbuf[MAXPATHLEN + 1];
870     char tmpbuf[MAXPATHLEN + 1];
871
872     static char *last_comp;     /* last component of directory name */
873     static char *elast_comp;    /* End of last component */
874     char *t;
875     int len;
876
877     static int symlinkcount = 0;        /* We can't exceed MAXSYMLINKS */
878
879     /* If we are given something for origpath, we are initializing only. */
880     if (origpath)
881     {
882         memset(path, 0, sizeof(path));
883         memset(pathtocheck, 0, sizeof(pathtocheck));
884         CopyPathColon(origpath, path, pathtocheck);
885         last_comp = path;
886         symlinkcount = 0;
887         return(NULL);
888     }
889
890     /* We were not given origpath; find then next path to check */
891
892     /* If we've gotten all the way through already, return NULL */
893     if (last_comp == NULL)
894         return(NULL);
895
896     do
897     {
898         while (BeginsWithDir(last_comp, FALSE))
899             strncat(pathtocheck, last_comp++, 1);
900         len = (elast_comp = LastComponent(last_comp))
901             ? elast_comp - last_comp : strlen(last_comp);
902         strncat(pathtocheck, last_comp, len);
903         memset(linkbuf, 0, sizeof(linkbuf));
904         if (link = (readlink(pathtocheck, linkbuf, sizeof(linkbuf)) > 0))
905         {
906             if (++symlinkcount > MAXSYMLINKS)
907             {
908                 fprintf(stderr, "%s: %s\n", progname, strerror(ELOOP));
909                 exit(AKLOG_BADPATH);
910             }
911             memset(tmpbuf, 0, sizeof(tmpbuf));
912             if (elast_comp)
913                 strcpy(tmpbuf, elast_comp);
914             if (BeginsWithDir(linkbuf, FALSE))
915             {
916                 /*
917                 * If this is a symbolic link to an absolute path,
918                 * replace what we have by the absolute path.
919                 */
920                 memset(path, 0, strlen(path));
921                 memcpy(path, linkbuf, sizeof(linkbuf));
922                 strcat(path, tmpbuf);
923                 last_comp = path;
924                 elast_comp = NULL;
925                 memset(pathtocheck, 0, sizeof(pathtocheck));
926             }
927             else
928             {
929                 /*
930                 * If this is a symbolic link to a relative path,
931                 * replace only the last component with the link name.
932                 */
933                 strncpy(last_comp, linkbuf, strlen(linkbuf) + 1);
934                 strcat(path, tmpbuf);
935                 elast_comp = NULL;
936                 if (t = LastComponent(pathtocheck))
937                 {
938                     t++;
939                     memset(t, 0, strlen(t));
940                 }
941                 else
942                     memset(pathtocheck, 0, sizeof(pathtocheck));
943             }
944         }
945         else
946             last_comp = elast_comp;
947     }       
948     while(link);
949
950     return(pathtocheck);
951 }
952
953 /*
954 * This routine descends through a path to a directory, logging to
955 * every cell it encounters along the way.
956 */
957 static int auth_to_path(krb5_context context, char *path)
958 {
959     int status = AKLOG_SUCCESS;
960     int auth_to_cell_status = AKLOG_SUCCESS;
961
962     char *nextpath;
963     char pathtocheck[MAXPATHLEN + 1];
964     char mountpoint[MAXPATHLEN + 1];
965
966     char *cell;
967     char *endofcell;
968
969     /* Initialize */
970     if (BeginsWithDir(path, TRUE))
971         strcpy(pathtocheck, path);
972     else
973     {
974         if (getcwd(pathtocheck, sizeof(pathtocheck)) == NULL)
975         {
976             fprintf(stderr, "Unable to find current working directory:\n");
977             fprintf(stderr, "%s\n", pathtocheck);
978             fprintf(stderr, "Try an absolute pathname.\n");
979             exit(AKLOG_BADPATH);
980         }
981         else
982         {
983             /* in WIN32, if getcwd returns a root dir (eg: c:\), the returned string
984             * will already have a trailing slash ('\'). Otherwise, the string will
985             * end in the last directory name */
986 #ifdef WIN32    
987             if(pathtocheck[strlen(pathtocheck) - 1] != BDIR)
988 #endif  
989                 strcat(pathtocheck, DIRSTRING);
990             strcat(pathtocheck, path);
991         }
992     }
993     next_path(pathtocheck);
994
995     /* Go on to the next level down the path */
996     while (nextpath = next_path(NULL))
997     {
998         strcpy(pathtocheck, nextpath);
999         if (dflag)
1000             printf("Checking directory [%s]\n", pathtocheck);
1001         /*
1002         * If this is an afs mountpoint, determine what cell from
1003         * the mountpoint name which is of the form
1004         * #cellname:volumename or %cellname:volumename.
1005         */
1006         if (get_afs_mountpoint(pathtocheck, mountpoint, sizeof(mountpoint)))
1007         {
1008             if(dflag)
1009                 printf("Found mount point [%s]\n", mountpoint);
1010             /* skip over the '#' or '%' */
1011             cell = mountpoint + 1;
1012             if (endofcell = strchr(mountpoint, VOLMARKER))
1013             {
1014                 *endofcell = '\0';
1015                 if (auth_to_cell_status = auth_to_cell(context, cell, NULL))
1016                 {
1017                     if (status == AKLOG_SUCCESS)
1018                         status = auth_to_cell_status;
1019                     else if (status != auth_to_cell_status)
1020                         status = AKLOG_SOMETHINGSWRONG;
1021                 }
1022             }
1023         }
1024         else
1025         {
1026             struct stat st;
1027
1028             if (lstat(pathtocheck, &st) < 0)
1029             {
1030                 /*
1031                 * If we've logged and still can't stat, there's
1032                 * a problem...
1033                 */
1034                 fprintf(stderr, "%s: stat(%s): %s\n", progname,
1035                          pathtocheck, strerror(errno));
1036                 return(AKLOG_BADPATH);
1037             }
1038             else if (!S_ISDIR(st.st_mode))
1039             {
1040                 /* Allow only directories */
1041                 fprintf(stderr, "%s: %s: %s\n", progname, pathtocheck,
1042                          strerror(ENOTDIR));
1043                 return(AKLOG_BADPATH);
1044             }
1045         }
1046     }
1047
1048     return(status);
1049 }
1050
1051 /* Print usage message and exit */
1052 static void usage(void)
1053 {
1054     fprintf(stderr, "\nUsage: %s %s%s%s%s\n", progname,
1055              "[-d] [[-cell | -c] cell [-k krb_realm]] ",
1056              "[[-p | -path] pathname]\n",
1057              "    [-noprdb] [-force]\n",
1058              "    [-5 [-m]| -4]\n"
1059              );
1060     fprintf(stderr, "    -d gives debugging information.\n");
1061     fprintf(stderr, "    krb_realm is the kerberos realm of a cell.\n");
1062     fprintf(stderr, "    pathname is the name of a directory to which ");
1063     fprintf(stderr, "you wish to authenticate.\n");
1064     fprintf(stderr, "    -noprdb means don't try to determine AFS ID.\n");
1065     fprintf(stderr, "    -5 or -4 selects whether to use Kerberos V or Kerberos IV.\n"
1066                     "       (default is Kerberos V)\n");
1067     fprintf(stderr, "       -m means use krb524d to convert Kerberos V tickets.\n");
1068     fprintf(stderr, "    No commandline arguments means ");
1069     fprintf(stderr, "authenticate to the local cell.\n");
1070     fprintf(stderr, "\n");
1071     exit(AKLOG_USAGE);
1072 }
1073
1074 int main(int argc, char *argv[])
1075 {
1076     int status = AKLOG_SUCCESS;
1077     int i;
1078     int somethingswrong = FALSE;
1079
1080     cellinfo_t cellinfo;
1081
1082     extern char *progname;      /* Name of this program */
1083
1084     extern int dflag;           /* Debug mode */
1085
1086     int cmode = FALSE;          /* Cellname mode */
1087     int pmode = FALSE;          /* Path name mode */
1088
1089     char realm[REALM_SZ];               /* Kerberos realm of afs server */
1090     char cell[BUFSIZ];          /* Cell to which we are authenticating */
1091     char path[MAXPATHLEN + 1];  /* Path length for path mode */
1092
1093     linked_list cells;          /* List of cells to log to */
1094     linked_list paths;          /* List of paths to log to */
1095     ll_node *cur_node;
1096
1097     krb5_context context = 0;
1098
1099     memset(&cellinfo, 0, sizeof(cellinfo));
1100
1101     memset(realm, 0, sizeof(realm));
1102     memset(cell, 0, sizeof(cell));
1103     memset(path, 0, sizeof(path));
1104
1105     ll_init(&cells);
1106     ll_init(&paths);
1107
1108     /* Store the program name here for error messages */
1109     if (progname = LastComponent(argv[0]))
1110         progname++;
1111     else
1112         progname = argv[0];
1113
1114     /* Initialize list of cells to which we have authenticated */
1115     (void)ll_init(&authedcells);
1116
1117     /* Parse commandline arguments and make list of what to do. */
1118     for (i = 1; i < argc; i++)
1119     {
1120         if (strcmp(argv[i], "-d") == 0)
1121             dflag++;
1122         else if (strcmp(argv[i], "-5") == 0)
1123             usev5++;
1124         else if (strcmp(argv[i], "-m") == 0)
1125             use524++;
1126         else if (strcmp(argv[i], "-4") == 0)
1127             usev5 = 0;
1128         else if (strcmp(argv[i], "-noprdb") == 0)
1129             noprdb++;
1130         else if (strcmp(argv[i], "-force") == 0)
1131             force++;
1132         else if (((strcmp(argv[i], "-cell") == 0) ||
1133                    (strcmp(argv[i], "-c") == 0)) && !pmode)
1134         {       
1135             if (++i < argc)
1136             {
1137                 cmode++;
1138                 strcpy(cell, argv[i]);
1139             }
1140             else
1141                 usage();
1142         }
1143         else if (((strcmp(argv[i], "-path") == 0) ||
1144                    (strcmp(argv[i], "-p") == 0)) && !cmode)
1145         {       
1146             if (++i < argc)
1147             {
1148                 pmode++;
1149                 strcpy(path, argv[i]);
1150             }
1151             else
1152                 usage();
1153         }
1154         else if (argv[i][0] == '-')
1155             usage();
1156         else if (!pmode && !cmode)
1157         {
1158             if (FirstComponent(argv[i]) || (strcmp(argv[i], ".") == 0) ||
1159                  (strcmp(argv[i], "..") == 0))
1160             {
1161                 pmode++;
1162                 strcpy(path, argv[i]);
1163             }
1164             else
1165             {
1166                 cmode++;
1167                 strcpy(cell, argv[i]);
1168             }
1169         }
1170         else
1171             usage();
1172
1173         if (cmode)
1174         {
1175             if (((i + 1) < argc) && (strcmp(argv[i + 1], "-k") == 0))
1176             {
1177                 i += 2;
1178                 if (i < argc)
1179                     strcpy(realm, argv[i]);
1180                 else
1181                     usage();
1182             }
1183             /* Add this cell to list of cells */
1184             strcpy(cellinfo.cell, cell);
1185             strcpy(cellinfo.realm, realm);
1186             if (cur_node = ll_add_node(&cells, ll_tail))
1187             {
1188                 char *new_cellinfo;
1189                 if (new_cellinfo = copy_cellinfo(&cellinfo))
1190                     ll_add_data(cur_node, new_cellinfo);
1191                 else
1192                 {
1193                     fprintf(stderr, "%s: failure copying cellinfo.\n", progname);
1194                     exit(AKLOG_MISC);
1195                 }
1196             }
1197             else
1198             {
1199                 fprintf(stderr, "%s: failure adding cell to cells list.\n",
1200                          progname);
1201                 exit(AKLOG_MISC);
1202             }
1203             memset(&cellinfo, 0, sizeof(cellinfo));
1204             cmode = FALSE;
1205             memset(cell, 0, sizeof(cell));
1206             memset(realm, 0, sizeof(realm));
1207         }
1208         else if (pmode)
1209         {
1210             /* Add this path to list of paths */
1211             if (cur_node = ll_add_node(&paths, ll_tail))
1212             {
1213                 char *new_path;
1214                 if (new_path = copy_string(path))
1215                     ll_add_data(cur_node, new_path);
1216                 else
1217                 {
1218                     fprintf(stderr, "%s: failure copying path name.\n",
1219                              progname);
1220                     exit(AKLOG_MISC);
1221                 }
1222             }
1223             else
1224             {
1225                 fprintf(stderr, "%s: failure adding path to paths list.\n",
1226                          progname);
1227                 exit(AKLOG_MISC);
1228             }
1229             pmode = FALSE;
1230             memset(path, 0, sizeof(path));
1231         }
1232     }
1233
1234     if(usev5)
1235         krb5_init_context(&context);
1236
1237     /* If nothing was given, log to the local cell. */
1238     if ((cells.nelements + paths.nelements) == 0)
1239         status = auth_to_cell(context, NULL, NULL);
1240     else
1241     {
1242         /* Log to all cells in the cells list first */
1243         for (cur_node = cells.first; cur_node; cur_node = cur_node->next)
1244         {
1245             memcpy(&cellinfo, cur_node->data, sizeof(cellinfo));
1246             if (status = auth_to_cell(context, 
1247                                        cellinfo.cell, cellinfo.realm))
1248                 somethingswrong++;
1249         }       
1250
1251         /* Then, log to all paths in the paths list */
1252         for (cur_node = paths.first; cur_node; cur_node = cur_node->next)
1253         {
1254             if (status = auth_to_path(context, 
1255                                        cur_node->data))
1256                 somethingswrong++;
1257         }
1258
1259         /*
1260         * If only one thing was logged to, we'll return the status
1261         * of the single call.  Otherwise, we'll return a generic
1262         * something failed status.
1263         */
1264         if (somethingswrong && ((cells.nelements + paths.nelements) > 1))
1265             status = AKLOG_SOMETHINGSWRONG;
1266     }       
1267
1268     if(usev5)
1269         krb5_free_context(context);
1270
1271     exit(status);
1272 }