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