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