35f60d3175283a97335c856dfa16922475e07ffe
[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 = 0;
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 (status == 0 && 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         int retry = 1;
530
531       try_v5:
532                 if (dflag)
533                         printf("Getting v5 tickets: %s/%s@%s\n", name, instance, realm_of_cell);
534                 status = get_v5cred(context, name, instance, realm_of_cell, NULL, &v5cred);
535                 if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
536                         if (dflag)
537                                 printf("Getting v5 tickets: %s@%s\n", name, realm_of_cell);
538                         status = get_v5cred(context, name, "", realm_of_cell, NULL, &v5cred);
539                 }
540         if ( status == KRB5KRB_AP_ERR_MSG_TYPE && retry ) {
541             retry = 0;
542             goto try_v5;
543         }
544         }
545         else 
546         {
547                 /*
548                 * Try to obtain AFS tickets.  Because there are two valid service
549                 * names, we will try both, but trying the more specific first.
550                 *
551                 *       afs.<cell>@<realm>
552                 *       afs@<realm>
553                 */
554                 if (dflag)
555                         printf("Getting tickets: %s.%s@%s\n", name, instance, realm_of_cell);
556                 status = get_cred(name, instance, realm_of_cell, &c);
557                 if (status == KDC_PR_UNKNOWN)
558                 {
559                         if (dflag)
560                                 printf("Getting tickets: %s@%s\n", name, realm_of_cell);
561                         status = get_cred(name, "", realm_of_cell, &c);
562                 }
563         } 
564
565         /* TODO: get k5 error text */
566         if (status != KSUCCESS)
567         {
568                 if (dflag)
569                         printf("Kerberos error code returned by get_cred: %d\n", status);
570                 fprintf(stderr, "%s: Couldn't get %s AFS tickets: %s\n",
571                         progname, cell_to_use, 
572                         (usev5)?"":
573                         krb_err_text(status));
574                 return(AKLOG_KERBEROS);
575         }
576
577         strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
578         strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
579         strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1);
580
581     if (usev5) {
582         /* This code inserts the entire K5 ticket into the token
583         * No need to perform a krb524 translation which is
584         * commented out in the code below
585         */
586         char * p;
587         int len;
588         
589         len = min(v5cred->client->data[0].length,MAXKTCNAMELEN - 1);
590         strncpy(username, v5cred->client->data[0].data, len);
591         username[len] = '\0';
592
593         if ( v5cred->client->length > 1 ) {
594             strcat(username, ".");
595             p = username + strlen(username);
596             len = min(v5cred->client->data[1].length,MAXKTCNAMELEN - strlen(username) - 1);
597             strncpy(p, v5cred->client->data[1].data, len);
598             p[len] = '\0';
599         }
600
601         memset(&atoken, '\0', sizeof(atoken));
602         atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
603         atoken.startTime = v5cred->times.starttime;
604         atoken.endTime = v5cred->times.endtime;
605         memcpy(&atoken.sessionKey, v5cred->keyblock.contents, v5cred->keyblock.length);
606         atoken.ticketLen = v5cred->ticket.length;
607         memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen);
608     } else 
609     {
610         strcpy (username, c.pname);
611         if (c.pinst[0])
612         {
613             strcat(username, ".");
614             strcat(username, c.pinst);
615         }
616
617         atoken.kvno = c.kvno;
618         atoken.startTime = c.issue_date;
619         /* ticket lifetime is in five-minutes blocks. */
620         atoken.endTime = c.issue_date + ((unsigned char)c.lifetime * 5 * 60);
621
622         memcpy(&atoken.sessionKey, c.session, 8);
623         atoken.ticketLen = c.ticket_st.length;
624         memcpy(atoken.ticket, c.ticket_st.dat, atoken.ticketLen);
625     }
626
627         if (!force &&
628                 !ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient) &&
629                 atoken.kvno == btoken.kvno &&
630                 atoken.ticketLen == btoken.ticketLen &&
631                 !memcmp(&atoken.sessionKey, &btoken.sessionKey, sizeof(atoken.sessionKey)) &&
632                 !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen))
633         {
634                 if (dflag)
635                         printf("Identical tokens already exist; skipping.\n");
636                 return 0;
637         }
638
639         if (noprdb)
640         {
641                 if (dflag)
642                         printf("Not resolving name %s to id (-noprdb set)\n", username);
643         }
644         else
645         {
646                 if(usev5) {
647                         if((status = get_v5_user_realm(context, realm_of_user)) != KSUCCESS) {
648                                 fprintf(stderr, "%s: Couldn't determine realm of user: %d\n",
649                                         progname, status);
650                                 return(AKLOG_KERBEROS);
651                         }
652                 } else 
653                 {
654                         if ((status = krb_get_tf_realm(TKT_FILE, realm_of_user)) != KSUCCESS)
655                         {
656                                 fprintf(stderr, "%s: Couldn't determine realm of user: %s)",
657                                         progname, krb_err_text(status));
658                                 return(AKLOG_KERBEROS);
659                         }
660                 }
661
662                 if (strcmp(realm_of_user, realm_of_cell))
663                 {
664                         strcat(username, "@");
665                         strcat(username, realm_of_user);
666                 }
667
668                 ViceIDToUsername(username, &status, &aserver);
669         }
670
671         if (dflag)
672                 printf("Set username to %s\n", username);
673
674         /* Reset the "aclient" structure before we call ktc_SetToken.
675         * This structure was first set by the ktc_GetToken call when
676         * we were comparing whether identical tokens already existed.
677         */
678         strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
679         strcpy(aclient.instance, "");
680     if (usev5) {
681         int len = min(v5cred->client->realm.length,MAXKTCNAMELEN - 1);
682         strncpy(aclient.cell, v5cred->client->realm.data, len);
683         aclient.cell[len] = '\0';
684     } else
685         strncpy(aclient.cell, c.realm, MAXKTCREALMLEN - 1);
686
687         if (dflag)
688                 printf("Getting tokens.\n");
689         if (status = ktc_SetToken(&aserver, &atoken, &aclient, 0))
690         {
691                 fprintf(stderr,
692                         "%s: unable to obtain tokens for cell %s (status: %d).\n",
693                         progname, cell_to_use, status);
694                 status = AKLOG_TOKEN;
695         }
696
697         return(status);
698 }
699
700 static int get_afs_mountpoint(char *file, char *mountpoint, int size)
701 {
702         char our_file[MAXPATHLEN + 1];
703         char *parent_dir;
704         char *last_component;
705         struct ViceIoctl vio;
706         char cellname[BUFSIZ];
707
708         memset(our_file, 0, sizeof(our_file));
709         strcpy(our_file, file);
710
711         if (last_component = LastComponent(our_file))
712         {
713                 *last_component++ = 0;
714                 parent_dir = our_file;
715         }
716         else
717         {
718                 last_component = our_file;
719                 parent_dir = ".";
720         }
721
722         memset(cellname, 0, sizeof(cellname));
723
724         vio.in = last_component;
725         vio.in_size = strlen(last_component)+1;
726         vio.out_size = size;
727         vio.out = mountpoint;
728
729         if (!pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, &vio, 0))
730         {
731                 if (strchr(mountpoint, VOLMARKER) == NULL)
732                 {
733                         vio.in = file;
734                         vio.in_size = strlen(file) + 1;
735                         vio.out_size = sizeof(cellname);
736                         vio.out = cellname;
737
738                         if (!pioctl(file, VIOC_FILE_CELL_NAME, &vio, 1))
739                         {
740                                 strcat(cellname, VOLMARKERSTRING);
741                                 strcat(cellname, mountpoint + 1);
742                                 memset(mountpoint + 1, 0, size - 1);
743                                 strcpy(mountpoint + 1, cellname);
744                         }
745                 }
746                 return(TRUE);
747         }
748         else {
749                 return(FALSE);
750         }
751 }
752
753 /*
754 * This routine each time it is called returns the next directory
755 * down a pathname.  It resolves all symbolic links.  The first time
756 * it is called, it should be called with the name of the path
757 * to be descended.  After that, it should be called with the arguemnt
758 * NULL.
759 */
760 static char *next_path(char *origpath)
761 {
762         static char path[MAXPATHLEN + 1];
763         static char pathtocheck[MAXPATHLEN + 1];
764
765         int link = FALSE;               /* Is this a symbolic link? */
766         char linkbuf[MAXPATHLEN + 1];
767         char tmpbuf[MAXPATHLEN + 1];
768
769         static char *last_comp; /* last component of directory name */
770         static char *elast_comp;        /* End of last component */
771         char *t;
772         int len;
773
774         static int symlinkcount = 0;    /* We can't exceed MAXSYMLINKS */
775
776         /* If we are given something for origpath, we are initializing only. */
777         if (origpath)
778         {
779                 memset(path, 0, sizeof(path));
780                 memset(pathtocheck, 0, sizeof(pathtocheck));
781                 CopyPathColon(origpath, path, pathtocheck);
782                 last_comp = path;
783                 symlinkcount = 0;
784                 return(NULL);
785         }
786
787         /* We were not given origpath; find then next path to check */
788
789         /* If we've gotten all the way through already, return NULL */
790         if (last_comp == NULL)
791                 return(NULL);
792
793         do
794         {
795                 while (BeginsWithDir(last_comp, FALSE))
796                         strncat(pathtocheck, last_comp++, 1);
797                 len = (elast_comp = LastComponent(last_comp))
798                         ? elast_comp - last_comp : strlen(last_comp);
799                 strncat(pathtocheck, last_comp, len);
800                 memset(linkbuf, 0, sizeof(linkbuf));
801                 if (link = (readlink(pathtocheck, linkbuf, sizeof(linkbuf)) > 0))
802                 {
803                         if (++symlinkcount > MAXSYMLINKS)
804                         {
805                                 fprintf(stderr, "%s: %s\n", progname, strerror(ELOOP));
806                                 exit(AKLOG_BADPATH);
807                         }
808                         memset(tmpbuf, 0, sizeof(tmpbuf));
809                         if (elast_comp)
810                                 strcpy(tmpbuf, elast_comp);
811                         if (BeginsWithDir(linkbuf, FALSE))
812                         {
813                                 /*
814                                 * If this is a symbolic link to an absolute path,
815                                 * replace what we have by the absolute path.
816                                 */
817                                 memset(path, 0, strlen(path));
818                                 memcpy(path, linkbuf, sizeof(linkbuf));
819                                 strcat(path, tmpbuf);
820                                 last_comp = path;
821                                 elast_comp = NULL;
822                                 memset(pathtocheck, 0, sizeof(pathtocheck));
823                         }
824                         else
825                         {
826                                 /*
827                                 * If this is a symbolic link to a relative path,
828                                 * replace only the last component with the link name.
829                                 */
830                                 strncpy(last_comp, linkbuf, strlen(linkbuf) + 1);
831                                 strcat(path, tmpbuf);
832                                 elast_comp = NULL;
833                                 if (t = LastComponent(pathtocheck))
834                                 {
835                                         t++;
836                                         memset(t, 0, strlen(t));
837                                 }
838                                 else
839                                         memset(pathtocheck, 0, sizeof(pathtocheck));
840                         }
841                 }
842                 else
843                         last_comp = elast_comp;
844         }
845         while(link);
846
847         return(pathtocheck);
848 }
849
850 /*
851 * This routine descends through a path to a directory, logging to
852 * every cell it encounters along the way.
853 */
854 static int auth_to_path(krb5_context context, char *path)
855 {
856         int status = AKLOG_SUCCESS;
857         int auth_to_cell_status = AKLOG_SUCCESS;
858
859         char *nextpath;
860         char pathtocheck[MAXPATHLEN + 1];
861         char mountpoint[MAXPATHLEN + 1];
862
863         char *cell;
864         char *endofcell;
865
866         /* Initialize */
867         if (BeginsWithDir(path, TRUE))
868                 strcpy(pathtocheck, path);
869         else
870         {
871                 if (getcwd(pathtocheck, sizeof(pathtocheck)) == NULL)
872                 {
873                         fprintf(stderr, "Unable to find current working directory:\n");
874                         fprintf(stderr, "%s\n", pathtocheck);
875                         fprintf(stderr, "Try an absolute pathname.\n");
876                         exit(AKLOG_BADPATH);
877                 }
878                 else
879                 {
880                         /* in WIN32, if getcwd returns a root dir (eg: c:\), the returned string
881                         * will already have a trailing slash ('\'). Otherwise, the string will
882                         * end in the last directory name */
883 #ifdef WIN32
884                         if(pathtocheck[strlen(pathtocheck) - 1] != BDIR)
885 #endif
886                                 strcat(pathtocheck, DIRSTRING);
887                         strcat(pathtocheck, path);
888                 }
889         }
890         next_path(pathtocheck);
891
892         /* Go on to the next level down the path */
893         while (nextpath = next_path(NULL))
894         {
895                 strcpy(pathtocheck, nextpath);
896                 if (dflag)
897                         printf("Checking directory [%s]\n", pathtocheck);
898                 /*
899                 * If this is an afs mountpoint, determine what cell from
900                 * the mountpoint name which is of the form
901                 * #cellname:volumename or %cellname:volumename.
902                 */
903                 if (get_afs_mountpoint(pathtocheck, mountpoint, sizeof(mountpoint)))
904                 {
905                         if(dflag)
906                                 printf("Found mount point [%s]\n", mountpoint);
907                         /* skip over the '#' or '%' */
908                         cell = mountpoint + 1;
909                         if (endofcell = strchr(mountpoint, VOLMARKER))
910                         {
911                                 *endofcell = '\0';
912                                 if (auth_to_cell_status = auth_to_cell(context, cell, NULL))
913                                 {
914                                         if (status == AKLOG_SUCCESS)
915                                                 status = auth_to_cell_status;
916                                         else if (status != auth_to_cell_status)
917                                                 status = AKLOG_SOMETHINGSWRONG;
918                                 }
919                         }
920                 }
921                 else
922                 {
923                         struct stat st;
924
925                         if (lstat(pathtocheck, &st) < 0)
926                         {
927                                 /*
928                                 * If we've logged and still can't stat, there's
929                                 * a problem...
930                                 */
931                                 fprintf(stderr, "%s: stat(%s): %s\n", progname,
932                                         pathtocheck, strerror(errno));
933                                 return(AKLOG_BADPATH);
934                         }
935                         else if (!S_ISDIR(st.st_mode))
936                         {
937                                 /* Allow only directories */
938                                 fprintf(stderr, "%s: %s: %s\n", progname, pathtocheck,
939                                         strerror(ENOTDIR));
940                                 return(AKLOG_BADPATH);
941                         }
942                 }
943         }
944
945         return(status);
946 }
947
948 /* Print usage message and exit */
949 static void usage(void)
950 {
951         fprintf(stderr, "\nUsage: %s %s%s%s%s\n", progname,
952                 "[-d] [[-cell | -c] cell [-k krb_realm]] ",
953                 "[[-p | -path] pathname]\n",
954                 "    [-noprdb] [-force]\n",
955                 "    [-5 | -4]\n"
956                 );
957         fprintf(stderr, "    -d gives debugging information.\n");
958         fprintf(stderr, "    krb_realm is the kerberos realm of a cell.\n");
959         fprintf(stderr, "    pathname is the name of a directory to which ");
960         fprintf(stderr, "you wish to authenticate.\n");
961         fprintf(stderr, "    -noprdb means don't try to determine AFS ID.\n");
962         fprintf(stderr, "    -5 or -4 selects whether to use Kerberos V or Kerberos IV.\n"
963                                         "       (default is Kerberos V)\n");
964         fprintf(stderr, "    No commandline arguments means ");
965         fprintf(stderr, "authenticate to the local cell.\n");
966         fprintf(stderr, "\n");
967         exit(AKLOG_USAGE);
968 }
969
970 int main(int argc, char *argv[])
971 {
972         int status = AKLOG_SUCCESS;
973         int i;
974         int somethingswrong = FALSE;
975
976         cellinfo_t cellinfo;
977
978         extern char *progname;  /* Name of this program */
979
980         extern int dflag;               /* Debug mode */
981
982         int cmode = FALSE;              /* Cellname mode */
983         int pmode = FALSE;              /* Path name mode */
984
985         char realm[REALM_SZ];           /* Kerberos realm of afs server */
986         char cell[BUFSIZ];              /* Cell to which we are authenticating */
987         char path[MAXPATHLEN + 1];      /* Path length for path mode */
988
989         linked_list cells;              /* List of cells to log to */
990         linked_list paths;              /* List of paths to log to */
991         ll_node *cur_node;
992
993         krb5_context context = 0;
994
995         memset(&cellinfo, 0, sizeof(cellinfo));
996
997         memset(realm, 0, sizeof(realm));
998         memset(cell, 0, sizeof(cell));
999         memset(path, 0, sizeof(path));
1000
1001         ll_init(&cells);
1002         ll_init(&paths);
1003
1004         /* Store the program name here for error messages */
1005         if (progname = LastComponent(argv[0]))
1006                 progname++;
1007         else
1008                 progname = argv[0];
1009
1010         /* Initialize list of cells to which we have authenticated */
1011         (void)ll_init(&authedcells);
1012
1013         /* Parse commandline arguments and make list of what to do. */
1014         for (i = 1; i < argc; i++)
1015         {
1016                 if (strcmp(argv[i], "-d") == 0)
1017                         dflag++;
1018                 else if (strcmp(argv[i], "-5") == 0)
1019                         usev5++;
1020                 else if (strcmp(argv[i], "-4") == 0)
1021                         usev5 = 0;
1022                 else if (strcmp(argv[i], "-noprdb") == 0)
1023                         noprdb++;
1024                 else if (strcmp(argv[i], "-force") == 0)
1025                         force++;
1026                 else if (((strcmp(argv[i], "-cell") == 0) ||
1027                         (strcmp(argv[i], "-c") == 0)) && !pmode)
1028                 {
1029                         if (++i < argc)
1030                         {
1031                                 cmode++;
1032                                 strcpy(cell, argv[i]);
1033                         }
1034                         else
1035                                 usage();
1036                 }
1037                 else if (((strcmp(argv[i], "-path") == 0) ||
1038                         (strcmp(argv[i], "-p") == 0)) && !cmode)
1039                 {
1040                         if (++i < argc)
1041                         {
1042                                 pmode++;
1043                                 strcpy(path, argv[i]);
1044                         }
1045                         else
1046                                 usage();
1047                 }
1048                 else if (argv[i][0] == '-')
1049                         usage();
1050                 else if (!pmode && !cmode)
1051                 {
1052                         if (FirstComponent(argv[i]) || (strcmp(argv[i], ".") == 0) ||
1053                                 (strcmp(argv[i], "..") == 0))
1054                         {
1055                                 pmode++;
1056                                 strcpy(path, argv[i]);
1057                         }
1058                         else
1059                         {
1060                                 cmode++;
1061                                 strcpy(cell, argv[i]);
1062                         }
1063                 }
1064                 else
1065                         usage();
1066
1067                 if (cmode)
1068                 {
1069                         if (((i + 1) < argc) && (strcmp(argv[i + 1], "-k") == 0))
1070                         {
1071                                 i += 2;
1072                                 if (i < argc)
1073                                         strcpy(realm, argv[i]);
1074                                 else
1075                                         usage();
1076                         }
1077                         /* Add this cell to list of cells */
1078                         strcpy(cellinfo.cell, cell);
1079                         strcpy(cellinfo.realm, realm);
1080                         if (cur_node = ll_add_node(&cells, ll_tail))
1081                         {
1082                                 char *new_cellinfo;
1083                                 if (new_cellinfo = copy_cellinfo(&cellinfo))
1084                                         ll_add_data(cur_node, new_cellinfo);
1085                                 else
1086                                 {
1087                                         fprintf(stderr, "%s: failure copying cellinfo.\n", progname);
1088                                         exit(AKLOG_MISC);
1089                                 }
1090                         }
1091                         else
1092                         {
1093                                 fprintf(stderr, "%s: failure adding cell to cells list.\n",
1094                                         progname);
1095                                 exit(AKLOG_MISC);
1096                         }
1097                         memset(&cellinfo, 0, sizeof(cellinfo));
1098                         cmode = FALSE;
1099                         memset(cell, 0, sizeof(cell));
1100                         memset(realm, 0, sizeof(realm));
1101                 }
1102                 else if (pmode)
1103                 {
1104                         /* Add this path to list of paths */
1105                         if (cur_node = ll_add_node(&paths, ll_tail))
1106                         {
1107                                 char *new_path;
1108                                 if (new_path = copy_string(path))
1109                                         ll_add_data(cur_node, new_path);
1110                                 else
1111                                 {
1112                                         fprintf(stderr, "%s: failure copying path name.\n",
1113                                                 progname);
1114                                         exit(AKLOG_MISC);
1115                                 }
1116                         }
1117                         else
1118                         {
1119                                 fprintf(stderr, "%s: failure adding path to paths list.\n",
1120                                         progname);
1121                                 exit(AKLOG_MISC);
1122                         }
1123                         pmode = FALSE;
1124                         memset(path, 0, sizeof(path));
1125                 }
1126         }
1127
1128         if(usev5)
1129                 krb5_init_context(&context);
1130
1131         /* If nothing was given, log to the local cell. */
1132         if ((cells.nelements + paths.nelements) == 0)
1133                 status = auth_to_cell(context, NULL, NULL);
1134         else
1135         {
1136                 /* Log to all cells in the cells list first */
1137                 for (cur_node = cells.first; cur_node; cur_node = cur_node->next)
1138                 {
1139                         memcpy(&cellinfo, cur_node->data, sizeof(cellinfo));
1140                         if (status = auth_to_cell(
1141                                 context, 
1142                                 cellinfo.cell, cellinfo.realm))
1143                                 somethingswrong++;
1144                 }
1145
1146                 /* Then, log to all paths in the paths list */
1147                 for (cur_node = paths.first; cur_node; cur_node = cur_node->next)
1148                 {
1149                         if (status = auth_to_path(
1150                                 context, 
1151                                 cur_node->data))
1152                                 somethingswrong++;
1153                 }
1154
1155                 /*
1156                 * If only one thing was logged to, we'll return the status
1157                 * of the single call.  Otherwise, we'll return a generic
1158                 * something failed status.
1159                 */
1160                 if (somethingswrong && ((cells.nelements + paths.nelements) > 1))
1161                         status = AKLOG_SOMETHINGSWRONG;
1162         }
1163
1164         if(usev5)
1165                 krb5_free_context(context);
1166
1167         exit(status);
1168 }