windows-aklog-linked-cells-20081106
[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 /*
17  * Copyright (c) 2007-2008 Secure Endpoints Inc.
18  *
19  * All rights reserved.
20  *
21  * Redistribution and use in source and binary forms, with or without
22  * modification, are permitted provided that the following conditions
23  * are met:
24  *
25  *     * Redistributions of source code must retain the above copyright
26  *       notice, this list of conditions and the following disclaimer.
27  *     * Neither the name of the Secure Endpoints Inc. nor the names of its
28  *       contributors may be used to endorse or promote products derived
29  *       from this software without specific prior written permission.
30  *
31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
37  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
38  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
39  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42  */
43
44 #ifndef _WIN64
45 #define HAVE_KRB4
46 #endif
47
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <ctype.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <errno.h>
55 #include <afs/stds.h>
56 #ifdef HAVE_KRB4
57 #include <krb.h>
58 #else
59 #define REALM_SZ 64
60 #define ANAME_SZ 64
61 #define INST_SZ  64
62 #define KSUCCESS 0
63
64 #define CREDENTIALS void
65 #endif
66 #include <krb5.h>
67 #include <afs/ptserver.h>
68 #include <afs/ptuser.h>
69
70 #ifdef WIN32
71 #include <windows.h>
72
73 #include <cm_config.h>
74 #include <auth.h>
75 #include <cellconfig.h>
76 #include <pioctl_nt.h>
77 #include <smb_iocons.h>
78
79 #define stat _stat
80 #define lstat stat
81 #define __S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
82 #define S_ISDIR(mode)          __S_ISTYPE((mode), _S_IFDIR)
83
84 #define DONT_HAVE_GET_AD_TKT
85 #define MAXSYMLINKS 255
86
87 #ifdef HAVE_KRB4
88 /* Win32 uses get_krb_err_txt_entry(status) instead of krb_err_txt[status],
89 * so we use a bit of indirection like the GNU CVS sources.
90 */
91 #define krb_err_text(status) get_krb_err_txt_entry(status)
92 #endif
93
94 #define DRIVECOLON ':'          /* Drive letter separator */
95 #define BDIR '\\'               /* Other character that divides directories */
96
97 static int 
98 readlink(char *path, char *buf, int buffers)
99 {
100         return -1;
101 }
102
103 char * getcwd(char*, size_t);
104
105 static long 
106 get_cellconfig_callback(void *cellconfig, struct sockaddr_in *addrp, char *namep)
107 {
108         struct afsconf_cell *cc = (struct afsconf_cell *) cellconfig;
109
110         cc->hostAddr[cc->numServers] = *addrp;
111         strcpy(cc->hostName[cc->numServers], namep);
112         cc->numServers++;
113         return 0;
114 }
115
116 #else /* WIN32 */
117 #include <sys/param.h>
118 #include <netdb.h>
119 #include <arpa/inet.h>
120 #include <sys/socket.h>
121 #include <unistd.h>
122
123 #include <afs/param.h>
124 #include <afs/auth.h>
125 #include <afs/cellconfig.h>
126 #include <afs/vice.h>
127 #include <afs/venus.h>
128 #include <afs/ptserver.h>
129
130 #define krb_err_text(status) krb_err_txt[status]
131
132 /* Cheesy test for determining AFS 3.5. */
133 #ifndef AFSCONF_CLIENTNAME
134 #define AFS35
135 #endif
136
137 #ifdef AFS35
138 #include <afs/dirpath.h>
139 #else
140 #define AFSDIR_CLIENT_ETC_DIRPATH AFSCONF_CLIENTNAME
141 #endif
142
143 #endif /* WIN32 */
144
145 #include "linked_list.h"
146
147 #define AFSKEY "afs"
148 #define AFSINST ""
149
150 #define AKLOG_SUCCESS 0
151 #define AKLOG_USAGE 1
152 #define AKLOG_SOMETHINGSWRONG 2
153 #define AKLOG_AFS 3
154 #define AKLOG_KERBEROS 4
155 #define AKLOG_TOKEN 5
156 #define AKLOG_BADPATH 6
157 #define AKLOG_MISC 7
158 #define AKLOG_KFW_NOT_INSTALLED 8
159
160 #ifndef NULL
161 #define NULL 0
162 #endif
163
164 #ifndef TRUE
165 #define TRUE 1
166 #endif
167
168 #ifndef FALSE
169 #define FALSE 0
170 #endif
171
172 #ifndef MAXSYMLINKS
173 #define MAXSYMLINKS 15
174 #endif
175
176 #define DIR '/'                 /* Character that divides directories */
177 #define DIRSTRING "/"           /* String form of above */
178 #define VOLMARKER ':'           /* Character separating cellname from mntpt */
179 #define VOLMARKERSTRING ":"     /* String form of above */
180
181 typedef struct {
182     char cell[BUFSIZ];
183     char realm[REALM_SZ];
184 } cellinfo_t;
185
186
187 static char *progname = NULL;   /* Name of this program */
188 static int dflag = FALSE;       /* Give debugging information */
189 static int noprdb = FALSE;      /* Skip resolving name to id? */
190 static int force = FALSE;       /* Bash identical tokens? */
191 static linked_list authedcells; /* List of cells already logged to */
192
193 static int usev5 = TRUE;   /* use kerberos 5? */
194 static int use524 = FALSE;  /* use krb524? */
195 static krb5_context context = 0;
196 static krb5_ccache _krb425_ccache = 0;
197
198 void akexit(int exit_code)
199 {
200     if (_krb425_ccache)
201         krb5_cc_close(context, _krb425_ccache);
202     if (context)
203         krb5_free_context(context);
204     exit(exit_code);
205 }
206
207 long GetLocalCell(struct afsconf_dir **pconfigdir, char *local_cell)
208 {
209     if (!(*pconfigdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)))
210     {
211         fprintf(stderr, "%s: can't get afs configuration (afsconf_Open(%s))\n",
212                  progname, AFSDIR_CLIENT_ETC_DIRPATH);
213         akexit(AKLOG_AFS);
214     }
215
216     return afsconf_GetLocalCell(*pconfigdir, local_cell, MAXCELLCHARS);
217 }
218
219 long GetCellInfo(struct afsconf_dir **pconfigdir, char* cell, 
220 struct afsconf_cell **pcellconfig)
221 {
222     return afsconf_GetCellInfo(*pconfigdir, cell, NULL, *pcellconfig);
223 }
224
225 void CloseConf(struct afsconf_dir **pconfigdir)
226 {       
227     (void) afsconf_Close(*pconfigdir);
228 }
229
230 #define ALLOW_REGISTER 1
231 void ViceIDToUsername(char *username, char *realm_of_user, char *realm_of_cell,
232                       char * cell_to_use, CREDENTIALS *c,
233                       int *status, 
234                       struct ktc_principal *aclient, struct ktc_principal *aserver, struct ktc_token *atoken)
235 {
236     static char lastcell[MAXCELLCHARS+1] = { 0 };
237     static char confname[512] = { 0 };
238     char username_copy[BUFSIZ];
239     afs_int32 viceId;                   /* AFS uid of user */
240
241     if (confname[0] == '\0') {
242         strncpy(confname, AFSDIR_CLIENT_ETC_DIRPATH, sizeof(confname));
243         confname[sizeof(confname) - 2] = '\0';
244     }
245
246     if (dflag)
247         printf("About to resolve name %s to id\n", username);
248
249     strcpy(lastcell, aserver->cell);
250
251     if (!pr_Initialize (0, confname, aserver->cell)) {
252         char sname[PR_MAXNAMELEN], *at;
253
254         strncpy(sname, username, PR_MAXNAMELEN);
255         sname[PR_MAXNAMELEN-1] = '\0';
256
257         at = strchr(sname, '@');
258         if (at && !stricmp(at+1, realm_of_cell))
259             *at = '\0';
260         *status = pr_SNameToId (sname, &viceId);
261     }
262
263     if (dflag)
264     {
265         if (*status)
266             printf("Error %d\n", *status);
267         else
268             printf("Id %d\n", viceId);
269     }       
270
271     /*
272      * This code is taken from cklog -- it lets people
273      * automatically register with the ptserver in foreign cells
274      */
275
276 #ifdef ALLOW_REGISTER
277     if (*status == 0) {
278         if (viceId != ANONYMOUSID) {
279 #else /* ALLOW_REGISTER */
280             if ((*status == 0) && (viceId != ANONYMOUSID))
281 #endif /* ALLOW_REGISTER */
282             {
283 #ifdef AFS_ID_TO_NAME
284                 strncpy(username_copy, username, BUFSIZ);
285                 snprintf (username, BUFSIZ, "%s (AFS ID %d)", username_copy, (int) viceId);
286 #endif /* AFS_ID_TO_NAME */
287             }
288 #ifdef ALLOW_REGISTER
289         } else if (strcmp(realm_of_user, realm_of_cell) != 0) {
290             int i;
291             if (dflag) {
292                 printf("doing first-time registration of %s "
293                         "at %s\n", username, cell_to_use);
294             }
295             strncpy(aclient->name, username, MAXKTCNAMELEN - 1);
296             aclient->name[MAXKTCNAMELEN - 1] = '\0';
297             strcpy(aclient->instance, "");
298             strncpy(aclient->cell, cell_to_use, MAXKTCREALMLEN - 1);
299             aclient->cell[MAXKTCREALMLEN - 1] = '\0';
300
301             for ( i=0; aclient->cell[i]; i++ ) {
302                 if ( islower(aclient->cell[i]) )
303                     aclient->cell[i] = toupper(aclient->cell[i]);
304             }
305
306             if ((*status = ktc_SetToken(aserver, atoken, aclient, 0))) {
307                 printf("%s: unable to set tokens for cell %s "
308                         "(status: %d).\n", progname, cell_to_use, *status);
309                 *status = AKLOG_TOKEN;
310                 return ;
311             }
312
313             /*
314              * In case you're wondering, we don't need to change the
315              * filename here because we're still connecting to the
316              * same cell -- we're just using a different authentication
317              * level
318              */
319
320             if ((*status = pr_Initialize(1L, confname, aserver->cell))) {
321                 printf("Error %d\n", *status);
322                 return;
323             }
324
325             /* copy the name because pr_CreateUser lowercases the realm */
326             strncpy(username_copy, username, BUFSIZ);
327
328             *status = pr_CreateUser(username, &viceId);
329
330             /* and restore the name to the original state */
331             strncpy(username, username_copy, BUFSIZ);
332
333             if (*status) {
334                 printf("%s: unable to create remote PTS "
335                         "user %s in cell %s (status: %d).\n", progname,
336                         username, cell_to_use, *status);
337             } else {
338                 printf("created cross-cell entry for %s (Id %d) at %s\n",
339                         username, viceId, cell_to_use);
340 #ifdef AFS_ID_TO_NAME
341                 snprintf (username, BUFSIZ, "%s (AFS ID %d)", username_copy, (int) viceId);
342 #endif /* AFS_ID_TO_NAME */
343             }
344         }
345     }
346 #endif /* ALLOW_REGISTER */
347 }
348
349 char *LastComponent(char *str)
350 {
351     char *ret = strrchr(str, DIR);
352
353 #ifdef WIN32
354     if (!ret)
355         ret = strrchr(str, BDIR);
356 #endif
357     return ret;
358 }
359
360 int FirstComponent(char *str)
361 {
362     return (int)(
363 #ifdef WIN32
364                 strchr(str, BDIR) ||
365 #endif
366                 strchr(str, DIR));
367 }
368
369 void CopyPathColon(char *origpath, char *path, char *pathtocheck)
370 {
371 #ifdef WIN32
372     if (origpath[1] == DRIVECOLON)
373     {
374         strncpy(pathtocheck, origpath, 2);
375         strcpy(path, origpath+2);
376     }
377     else
378 #endif
379         strcpy(path, origpath);
380 }
381
382 int BeginsWithDir(char *str, int colon)
383 {
384     return (str[0] == DIR) ||
385 #ifdef WIN32
386         ((str[0] == BDIR) || (colon && str[1] == DRIVECOLON));
387 #else
388     FALSE;
389 #endif
390 }
391
392
393 /* This is a pretty gross hack.  Linking against the Transarc
394 * libraries pulls in some rxkad functions which use des.  (I don't
395 * think they ever get called.)  With Transarc-supplied libraries this
396 * creates a reliance on the symbol des_pcbc_init() which is only in
397 * Transarc's DES libraries (it's an exportability symbol-hiding
398 * thing), which we don't want to use because they don't work with
399 * MIT's krb4 routines.  So export a des_pcbc_init() symbol here so we
400 * don't have to link against Transarc's des library.
401 */
402 int des_pcbc_init()
403 {
404     abort();
405     return 0;   /* avoid warning */
406 }
407
408 #ifdef HAVE_KRB4
409 static int get_cred(char *name, char *inst, char *realm, CREDENTIALS *c)
410 {
411     int status;
412
413     status = krb_get_cred(name, inst, realm, c);
414     if (status != KSUCCESS)
415     {
416 #ifdef DONT_HAVE_GET_AD_TKT
417         KTEXT_ST ticket;
418         status = krb_mk_req(&ticket, name, inst, realm, 0);
419 #else
420         status = get_ad_tkt(name, inst, realm, 255);
421 #endif
422         if (status == KSUCCESS)
423             status = krb_get_cred(name, inst, realm, c);
424     }       
425
426     return (status);
427 }
428 #endif
429
430 static int get_v5cred(krb5_context context, 
431                       char *name, char *inst, char *realm, CREDENTIALS *c,
432                       krb5_creds **creds)
433 {
434     krb5_creds increds;
435     krb5_error_code r;
436     static krb5_principal client_principal = 0;
437
438     if (client_principal) {
439         krb5_free_principal(context, client_principal);
440         client_principal = 0;
441     }
442
443     memset((char *)&increds, 0, sizeof(increds));
444
445     if ((r = krb5_build_principal(context, &increds.server,
446                                   (int)strlen(realm), realm,
447                                   name,
448                                   (inst && strlen(inst)) ? inst : 0,
449                                   0))) {
450         return((int)r);
451     }
452
453     if (!_krb425_ccache) {
454         if ((r = krb5_cc_default(context, &_krb425_ccache)))
455             return ((int)r);
456     }
457     if (!client_principal) {
458         if ((r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal))) {
459             krb5_cc_close(context, _krb425_ccache);
460             return ((int)r);
461         }
462     }
463
464     increds.client = client_principal;
465     increds.times.endtime = 0;
466         /* Ask for DES since that is what V4 understands */
467     increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
468
469     r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
470     if (r) {
471         return((int)r);
472     }
473     /* This requires krb524d to be running with the KDC */
474     if (c != NULL)
475         r = krb5_524_convert_creds(context, *creds, c);
476
477     return((int)r);
478 }
479
480 #ifdef HAVE_KRB4
481 /* There is no header for this function.  It is supposed to be private */
482 int krb_get_admhst(char *h,char *r, int n);
483
484 static char *afs_realm_of_cell(struct afsconf_cell *cellconfig)
485 {
486     char krbhst[MAX_HSTNM];
487     static char krbrlm[REALM_SZ+1];
488
489     if (!cellconfig)
490         return 0;
491
492     strcpy(krbrlm, (char *) krb_realmofhost(cellconfig->hostName[0]));
493
494     if (krb_get_admhst(krbhst, krbrlm, 1) != KSUCCESS)
495     {
496         char *s = krbrlm;
497         char *t = cellconfig->name;
498         int c;
499
500         while (c = *t++)
501         {
502             if (islower(c))
503                 c = toupper(c);
504             *s++ = c;
505         }
506         *s++ = 0;
507     }
508     return krbrlm;
509 }
510 #endif
511
512 /* As of MIT Kerberos 1.6, krb5_get_host_realm() will return the NUL-string 
513  * if there is no domain_realm mapping for the hostname's domain.  This is 
514  * used as a trigger indicating that referrals should be used within the
515  * krb5_get_credentials() call.  However, if the KDC does not support referrals
516  * that will result in a KRB5_ERR_HOST_REALM_UNKNOWN error and we will have
517  * to manually fallback to mapping the domain of the host as a realm name.
518  * Hence, the new fallback parameter.
519  */
520 static char *afs_realm_of_cell5(krb5_context context, struct afsconf_cell *cellconfig, int fallback)
521 {
522     char ** krbrlms = 0;
523     static char krbrlm[REALM_SZ+1];
524     krb5_error_code status;
525
526     if (!cellconfig)
527         return 0;
528
529     if (fallback) {
530         char * p;
531         p = strchr(cellconfig->hostName[0], '.');
532         if (p++)
533             strcpy(krbrlm, p);
534         else
535             strcpy(krbrlm, cellconfig->name);
536         strupr(krbrlm);
537     } else {
538         status = krb5_get_host_realm( context, cellconfig->hostName[0], &krbrlms );
539         if (status == 0 && krbrlms && krbrlms[0]) {
540             strcpy(krbrlm, krbrlms[0]);
541         } else {
542             strcpy(krbrlm, cellconfig->name);
543             strupr(krbrlm);
544         }
545
546         if (krbrlms)
547             krb5_free_host_realm( context, krbrlms );
548     }
549     return krbrlm;
550 }       
551
552 static char *copy_cellinfo(cellinfo_t *cellinfo)
553 {
554     cellinfo_t *new_cellinfo;
555
556     if (new_cellinfo = (cellinfo_t *)malloc(sizeof(cellinfo_t)))
557         memcpy(new_cellinfo, cellinfo, sizeof(cellinfo_t));
558
559     return ((char *)new_cellinfo);
560 }
561
562
563 static int get_cellconfig(char *cell, struct afsconf_cell *cellconfig,
564                                                   char *local_cell)
565 {
566     int status = AKLOG_SUCCESS;
567     struct afsconf_dir *configdir = 0;
568
569     memset(local_cell, 0, sizeof(local_cell));
570     memset(cellconfig, 0, sizeof(*cellconfig));
571
572     if (GetLocalCell(&configdir, local_cell))
573     {
574         fprintf(stderr, "%s: can't determine local cell.\n", progname);
575         akexit(AKLOG_AFS);
576     }
577
578     if ((cell == NULL) || (cell[0] == 0))
579         cell = local_cell;
580
581     if (GetCellInfo(&configdir, cell, &cellconfig))
582     {
583         fprintf(stderr, "%s: Can't get information about cell %s.\n",
584                 progname, cell);
585         status = AKLOG_AFS;
586     }
587
588     if (cellconfig->linkedCell)
589         cellconfig->linkedCell = strdup(cellconfig->linkedCell);
590
591     CloseConf(&configdir);
592
593     return(status);
594 }
595
596 static int get_v5_user_realm(krb5_context context,char *realm)
597 {
598     static krb5_principal client_principal = 0;
599     krb5_error_code code;
600     int i;
601
602     if (!_krb425_ccache) {
603         code = krb5_cc_default(context, &_krb425_ccache);
604         if (code)
605             return(code);
606     }
607     if (!client_principal) {
608         code = krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
609         if (code)
610             return(code);
611     }
612     i = krb5_princ_realm(context, client_principal)->length;
613     if (i < REALM_SZ-1) i = REALM_SZ-1;
614     strncpy(realm,krb5_princ_realm(context, client_principal)->data,i);
615     realm[i] = 0;
616     return(KSUCCESS);
617 }
618
619 static void
620 copy_realm_of_ticket(krb5_context context, char * dest, size_t destlen, krb5_creds *v5cred) {
621     krb5_error_code code;
622     krb5_ticket *ticket;
623     size_t len;
624
625     code = krb5_decode_ticket(&v5cred->ticket, &ticket);
626     if (code == 0) {
627         len = krb5_princ_realm(context, ticket->server)->length;
628         if (len > destlen - 1)
629             len = destlen - 1;
630
631         strncpy(dest, krb5_princ_realm(context, ticket->server)->data, len);
632         dest[len] = '\0';
633
634         krb5_free_ticket(context, ticket);
635     }
636 }
637
638 /*
639 * Log to a cell.  If the cell has already been logged to, return without
640 * doing anything.  Otherwise, log to it and mark that it has been logged
641 * to.  */
642 static int auth_to_cell(krb5_context context, char *cell, char *realm)
643 {
644     int status = AKLOG_SUCCESS;
645     char username[BUFSIZ];        /* To hold client username structure */
646
647     char name[ANAME_SZ];          /* Name of afs key */
648     char instance[INST_SZ];       /* Instance of afs key */
649     char realm_of_user[REALM_SZ]; /* Kerberos realm of user */
650     char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
651     char local_cell[MAXCELLCHARS+1];
652     char cell_to_use[MAXCELLCHARS+1]; /* Cell to authenticate to */
653
654     krb5_creds *v5cred = NULL;
655 #ifdef HAVE_KRB4
656     CREDENTIALS c;
657 #endif
658     struct ktc_principal aserver;
659     struct ktc_principal aclient;
660     struct ktc_token atoken, btoken;
661     struct afsconf_cell ak_cellconfig; /* General information about the cell */
662     int i;
663     int getLinkedCell = 0;
664
665     /* try to avoid an expensive call to get_cellconfig */
666     if (cell && ll_string_check(&authedcells, cell))
667     {
668         if (dflag)
669             printf("Already authenticated to %s (or tried to)\n", cell);
670         return(AKLOG_SUCCESS);
671     }
672
673     memset(name, 0, sizeof(name));
674     memset(instance, 0, sizeof(instance));
675     memset(realm_of_user, 0, sizeof(realm_of_user));
676     memset(realm_of_cell, 0, sizeof(realm_of_cell));
677     memset(&ak_cellconfig, 0, sizeof(ak_cellconfig));
678
679     /* NULL or empty cell returns information on local cell */
680     if (status = get_cellconfig(cell, &ak_cellconfig, local_cell))
681         return(status);
682
683   linkedCell:
684     if (getLinkedCell)
685         strncpy(cell_to_use, ak_cellconfig.linkedCell, MAXCELLCHARS);
686     else
687         strncpy(cell_to_use, ak_cellconfig.name, MAXCELLCHARS);
688     cell_to_use[MAXCELLCHARS] = 0;
689
690     if (ll_string_check(&authedcells, cell_to_use))
691     {
692         if (dflag)
693             printf("Already authenticated to %s (or tried to)\n", cell_to_use);
694         status = AKLOG_SUCCESS;
695         goto done2;
696     }
697
698     /*
699      * Record that we have attempted to log to this cell.  We do this
700      * before we try rather than after so that we will not try
701      * and fail repeatedly for one cell.
702      */
703     (void)ll_add_string(&authedcells, cell_to_use);
704
705     if (dflag)
706         printf("Authenticating to cell %s.\n", cell_to_use);
707
708     /* We use the afs.<cellname> convention here... */
709     strcpy(name, AFSKEY);
710     strncpy(instance, cell_to_use, sizeof(instance));
711     instance[sizeof(instance)-1] = '\0';
712
713     /*
714      * Extract the session key from the ticket file and hand-frob an
715      * afs style authenticator.
716      */
717
718     if (usev5) 
719     { /* using krb5 */
720         int retry = 1;
721         int realm_fallback = 0;
722
723         if ((status = get_v5_user_realm(context, realm_of_user)) != KSUCCESS) {
724             fprintf(stderr, "%s: Couldn't determine realm of user: %d\n",
725                      progname, status);
726             status = AKLOG_KERBEROS;
727             goto done;
728         }
729
730         if ( strchr(name,'.') != NULL ) {
731             fprintf(stderr, "%s: Can't support principal names including a dot.\n",
732                     progname);
733             status = AKLOG_MISC;
734             goto done;
735         }
736
737       try_v5:
738         if (realm && realm[0]) {
739             if (dflag)
740                 printf("Getting v5 tickets: %s/%s@%s\n", name, instance, realm);
741             status = get_v5cred(context, name, instance, realm, 
742 #ifdef HAVE_KRB4
743                             use524 ? &c : NULL, 
744 #else
745                             NULL,
746 #endif
747                             &v5cred);
748             strcpy(realm_of_cell, realm);
749         } else {
750             strcpy(realm_of_cell,
751                     afs_realm_of_cell5(context, &ak_cellconfig, realm_fallback));
752
753             if (retry == 1 && realm_fallback == 0) {
754                 /* Only try the realm_of_user once */
755                 status = -1;
756                 if (dflag)
757                     printf("Getting v5 tickets: %s/%s@%s\n", name, instance, realm_of_user);
758                 status = get_v5cred(context, name, instance, realm_of_user, 
759 #ifdef HAVE_KRB4
760                                      use524 ? &c : NULL, 
761 #else
762                                      NULL,
763 #endif
764                                      &v5cred);
765                 if (status == 0) {
766                     /* we have determined that the client realm 
767                      * is a valid cell realm
768                      */
769                     strcpy(realm_of_cell, realm_of_user);
770                 }
771             }
772
773             if (status != 0 && (!retry || retry && strcmp(realm_of_user,realm_of_cell))) {
774                 if (dflag)
775                     printf("Getting v5 tickets: %s/%s@%s\n", name, instance, realm_of_cell);
776                 status = get_v5cred(context, name, instance, realm_of_cell, 
777 #ifdef HAVE_KRB4
778                                      use524 ? &c : NULL, 
779 #else
780                                      NULL,
781 #endif
782                                      &v5cred);
783                 if (!status && !strlen(realm_of_cell)) 
784                     copy_realm_of_ticket(context, realm_of_cell, sizeof(realm_of_cell), v5cred);
785             }
786         }
787
788         if (!realm_fallback && status == KRB5_ERR_HOST_REALM_UNKNOWN) {
789             realm_fallback = 1;
790             goto try_v5;
791         } else if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
792             if (!realm_fallback && !realm_of_cell[0]) {
793                 realm_fallback = 1;
794                 goto try_v5;
795             }
796             if (dflag)
797                 printf("Getting v5 tickets: %s@%s\n", name, realm_of_cell);
798             status = get_v5cred(context, name, "", realm_of_cell, 
799 #ifdef HAVE_KRB4
800                                 use524 ? &c : NULL, 
801 #else
802                                 NULL,
803 #endif
804                                 &v5cred);
805             if (!status && !strlen(realm_of_cell)) 
806                 copy_realm_of_ticket(context, realm_of_cell, sizeof(realm_of_cell), v5cred);
807         }
808      
809         if ( status == KRB5KRB_AP_ERR_MSG_TYPE && retry ) {
810             retry = 0;
811             realm_fallback = 0;
812             goto try_v5;
813         }       
814     }       
815     else 
816     {
817 #ifdef HAVE_KRB4
818         if (realm && realm[0])
819             strcpy(realm_of_cell, realm);
820         else
821             strcpy(realm_of_cell, afs_realm_of_cell(&ak_cellconfig));
822
823         /*
824          * Try to obtain AFS tickets.  Because there are two valid service
825          * names, we will try both, but trying the more specific first.
826          *
827          *      afs.<cell>@<realm>
828          *      afs@<realm>
829          */
830         if (dflag)
831             printf("Getting tickets: %s.%s@%s\n", name, instance, realm_of_cell);
832         status = get_cred(name, instance, realm_of_cell, &c);
833         if (status == KDC_PR_UNKNOWN)
834         {
835             if (dflag)
836                 printf("Getting tickets: %s@%s\n", name, realm_of_cell);
837             status = get_cred(name, "", realm_of_cell, &c);
838         }
839 #else
840         status = AKLOG_MISC;
841         goto done;
842 #endif
843     } 
844
845     /* TODO: get k5 error text */
846     if (status != KSUCCESS)
847     {
848         if (dflag)
849             printf("Kerberos error code returned by get_cred: %d\n", status);
850         fprintf(stderr, "%s: Couldn't get %s AFS tickets: %s\n",
851                  progname, cell_to_use, 
852 #ifdef HAVE_KRB4
853                  (usev5)?"":krb_err_text(status)
854 #else
855                  ""
856 #endif
857                  );
858         status = AKLOG_KERBEROS;
859         goto done;
860     }
861
862     strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
863     strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
864     strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1);
865
866     if (usev5 && !use524) {
867         /* This code inserts the entire K5 ticket into the token
868          * No need to perform a krb524 translation which is
869          * commented out in the code below
870          */
871         char * p;
872         int len;
873         
874         len = min(v5cred->client->data[0].length,MAXKTCNAMELEN - 1);
875         strncpy(username, v5cred->client->data[0].data, len);
876         username[len] = '\0';
877
878         if ( v5cred->client->length > 1 ) {
879             strcat(username, ".");
880             p = username + strlen(username);
881             len = min(v5cred->client->data[1].length,MAXKTCNAMELEN - strlen(username) - 1);
882             strncpy(p, v5cred->client->data[1].data, len);
883             p[len] = '\0';
884         }
885
886         memset(&atoken, '\0', sizeof(atoken));
887         atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
888         atoken.startTime = v5cred->times.starttime;
889         atoken.endTime = v5cred->times.endtime;
890         memcpy(&atoken.sessionKey, v5cred->keyblock.contents, v5cred->keyblock.length);
891         atoken.ticketLen = v5cred->ticket.length;
892         memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen);
893     } else {
894 #ifdef HAVE_KRB4
895         strcpy (username, c.pname);
896         if (c.pinst[0])
897         {
898             strcat(username, ".");
899             strcat(username, c.pinst);
900         }
901
902         atoken.kvno = c.kvno;
903         atoken.startTime = c.issue_date;
904         /* ticket lifetime is in five-minutes blocks. */
905         atoken.endTime = c.issue_date + ((unsigned char)c.lifetime * 5 * 60);
906
907         memcpy(&atoken.sessionKey, c.session, 8);
908         atoken.ticketLen = c.ticket_st.length;
909         memcpy(atoken.ticket, c.ticket_st.dat, atoken.ticketLen);
910 #else
911         status = AKLOG_MISC;
912         goto done;
913 #endif
914     }
915
916     if (!force &&
917          !ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient) &&
918          atoken.kvno == btoken.kvno &&
919          atoken.ticketLen == btoken.ticketLen &&
920          !memcmp(&atoken.sessionKey, &btoken.sessionKey, sizeof(atoken.sessionKey)) &&
921          !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen))
922     {       
923         if (dflag)
924             printf("Identical tokens already exist; skipping.\n");
925         status = AKLOG_SUCCESS;
926         goto done2;
927     }
928
929     if (noprdb)
930     {
931         if (dflag)
932             printf("Not resolving name %s to id (-noprdb set)\n", username);
933     }       
934     else    
935     {
936         if (!usev5) {
937 #ifdef HAVE_KRB4
938             if ((status = krb_get_tf_realm(TKT_FILE, realm_of_user)) != KSUCCESS)
939             {
940                 fprintf(stderr, "%s: Couldn't determine realm of user: %s)",
941                          progname, krb_err_text(status));
942                 status = AKLOG_KERBEROS;
943                 goto done;
944             }
945 #else
946             status = AKLOG_MISC;
947             goto done;
948 #endif
949         }
950
951         /* For Network Identity Manager append the realm to the name */
952         strcat(username, "@");
953         strcat(username, realm_of_user);
954
955         ViceIDToUsername(username, realm_of_user, realm_of_cell, cell_to_use, 
956 #ifdef HAVE_KRB4
957                           &c, 
958 #else
959                           NULL,
960 #endif
961                           &status, &aclient, &aserver, &atoken);
962     }
963
964     if (dflag)
965         printf("Set username to %s\n", username);
966
967     /* Reset the "aclient" structure before we call ktc_SetToken.
968      * This structure was first set by the ktc_GetToken call when
969      * we were comparing whether identical tokens already existed.
970      */
971     strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
972     strcpy(aclient.instance, "");
973     
974     if (usev5 && !use524) {
975         int len = min(v5cred->client->realm.length,MAXKTCNAMELEN - 1);
976         strncpy(aclient.cell, v5cred->client->realm.data, len);
977         aclient.cell[len] = '\0';
978     } 
979 #ifdef HAVE_KRB4
980     else
981         strncpy(aclient.cell, c.realm, MAXKTCREALMLEN - 1);
982 #endif
983
984     for ( i=0; aclient.cell[i]; i++ ) {
985         if ( islower(aclient.cell[i]) )
986             aclient.cell[i] = toupper(aclient.cell[i]);
987     }
988
989     if (dflag)
990         printf("Getting tokens.\n");
991     if (status = ktc_SetToken(&aserver, &atoken, &aclient, 0))
992     {
993         fprintf(stderr,
994                  "%s: unable to obtain tokens for cell %s (status: %d).\n",
995                  progname, cell_to_use, status);
996         status = AKLOG_TOKEN;
997     }
998
999   done2:
1000     if (ak_cellconfig.linkedCell && !getLinkedCell) {
1001         getLinkedCell = 1;
1002         goto linkedCell;
1003     }
1004
1005   done:
1006 #if 0
1007     /* 
1008      * intentionally leak the linkedCell field because it was allocated
1009      * using a different C RTL version.
1010      */
1011     if (ak_cellconfig.linkedCell)
1012         free(ak_cellconfig.linkedCell);
1013 #endif
1014     return(status);
1015 }
1016
1017 static int get_afs_mountpoint(char *file, char *mountpoint, int size)
1018 {
1019     char our_file[MAXPATHLEN + 1];
1020     char *parent_dir;
1021     char *last_component;
1022     struct ViceIoctl vio;
1023     char cellname[BUFSIZ];
1024
1025     memset(our_file, 0, sizeof(our_file));
1026     strcpy(our_file, file);
1027
1028     if (last_component = LastComponent(our_file))
1029     {
1030         *last_component++ = 0;
1031         parent_dir = our_file;
1032     }
1033     else
1034     {
1035         last_component = our_file;
1036         parent_dir = ".";
1037     }
1038
1039     memset(cellname, 0, sizeof(cellname));
1040
1041     vio.in = last_component;
1042     vio.in_size = strlen(last_component)+1;
1043     vio.out_size = size;
1044     vio.out = mountpoint;
1045
1046     if (!pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, &vio, 0))
1047     {
1048         if (strchr(mountpoint, VOLMARKER) == NULL)
1049         {
1050             vio.in = file;
1051             vio.in_size = strlen(file) + 1;
1052             vio.out_size = sizeof(cellname);
1053             vio.out = cellname;
1054
1055             if (!pioctl(file, VIOC_FILE_CELL_NAME, &vio, 1))
1056             {
1057                 strcat(cellname, VOLMARKERSTRING);
1058                 strcat(cellname, mountpoint + 1);
1059                 memset(mountpoint + 1, 0, size - 1);
1060                 strcpy(mountpoint + 1, cellname);
1061             }
1062         }
1063         return(TRUE);
1064     }
1065     else {
1066         return(FALSE);
1067     }
1068 }       
1069
1070 /*
1071 * This routine each time it is called returns the next directory
1072 * down a pathname.  It resolves all symbolic links.  The first time
1073 * it is called, it should be called with the name of the path
1074 * to be descended.  After that, it should be called with the arguemnt
1075 * NULL.
1076 */
1077 static char *next_path(char *origpath)
1078 {
1079     static char path[MAXPATHLEN + 1];
1080     static char pathtocheck[MAXPATHLEN + 1];
1081
1082     int link = FALSE;           /* Is this a symbolic link? */
1083     char linkbuf[MAXPATHLEN + 1];
1084     char tmpbuf[MAXPATHLEN + 1];
1085
1086     static char *last_comp;     /* last component of directory name */
1087     static char *elast_comp;    /* End of last component */
1088     char *t;
1089     int len;
1090
1091     static int symlinkcount = 0;        /* We can't exceed MAXSYMLINKS */
1092
1093     /* If we are given something for origpath, we are initializing only. */
1094     if (origpath)
1095     {
1096         memset(path, 0, sizeof(path));
1097         memset(pathtocheck, 0, sizeof(pathtocheck));
1098         CopyPathColon(origpath, path, pathtocheck);
1099         last_comp = path;
1100         symlinkcount = 0;
1101         return(NULL);
1102     }
1103
1104     /* We were not given origpath; find then next path to check */
1105
1106     /* If we've gotten all the way through already, return NULL */
1107     if (last_comp == NULL)
1108         return(NULL);
1109
1110     do
1111     {
1112         while (BeginsWithDir(last_comp, FALSE))
1113             strncat(pathtocheck, last_comp++, 1);
1114         len = (elast_comp = LastComponent(last_comp))
1115             ? elast_comp - last_comp : strlen(last_comp);
1116         strncat(pathtocheck, last_comp, len);
1117         memset(linkbuf, 0, sizeof(linkbuf));
1118         if (link = (readlink(pathtocheck, linkbuf, sizeof(linkbuf)) > 0))
1119         {
1120             if (++symlinkcount > MAXSYMLINKS)
1121             {
1122                 fprintf(stderr, "%s: %s\n", progname, strerror(ELOOP));
1123                 akexit(AKLOG_BADPATH);
1124             }
1125             memset(tmpbuf, 0, sizeof(tmpbuf));
1126             if (elast_comp)
1127                 strcpy(tmpbuf, elast_comp);
1128             if (BeginsWithDir(linkbuf, FALSE))
1129             {
1130                 /*
1131                 * If this is a symbolic link to an absolute path,
1132                 * replace what we have by the absolute path.
1133                 */
1134                 memset(path, 0, strlen(path));
1135                 memcpy(path, linkbuf, sizeof(linkbuf));
1136                 strcat(path, tmpbuf);
1137                 last_comp = path;
1138                 elast_comp = NULL;
1139                 memset(pathtocheck, 0, sizeof(pathtocheck));
1140             }
1141             else
1142             {
1143                 /*
1144                 * If this is a symbolic link to a relative path,
1145                 * replace only the last component with the link name.
1146                 */
1147                 strncpy(last_comp, linkbuf, strlen(linkbuf) + 1);
1148                 strcat(path, tmpbuf);
1149                 elast_comp = NULL;
1150                 if (t = LastComponent(pathtocheck))
1151                 {
1152                     t++;
1153                     memset(t, 0, strlen(t));
1154                 }
1155                 else
1156                     memset(pathtocheck, 0, sizeof(pathtocheck));
1157             }
1158         }
1159         else
1160             last_comp = elast_comp;
1161     }       
1162     while(link);
1163
1164     return(pathtocheck);
1165 }
1166
1167 /*
1168 * This routine descends through a path to a directory, logging to
1169 * every cell it encounters along the way.
1170 */
1171 static int auth_to_path(krb5_context context, char *path)
1172 {
1173     int status = AKLOG_SUCCESS;
1174     int auth_to_cell_status = AKLOG_SUCCESS;
1175
1176     char *nextpath;
1177     char pathtocheck[MAXPATHLEN + 1];
1178     char mountpoint[MAXPATHLEN + 1];
1179
1180     char *cell;
1181     char *endofcell;
1182
1183     /* Initialize */
1184     if (BeginsWithDir(path, TRUE))
1185         strcpy(pathtocheck, path);
1186     else
1187     {
1188         if (getcwd(pathtocheck, sizeof(pathtocheck)) == NULL)
1189         {
1190             fprintf(stderr, "Unable to find current working directory:\n");
1191             fprintf(stderr, "%s\n", pathtocheck);
1192             fprintf(stderr, "Try an absolute pathname.\n");
1193             akexit(AKLOG_BADPATH);
1194         }
1195         else
1196         {
1197             /* in WIN32, if getcwd returns a root dir (eg: c:\), the returned string
1198             * will already have a trailing slash ('\'). Otherwise, the string will
1199             * end in the last directory name */
1200 #ifdef WIN32    
1201             if(pathtocheck[strlen(pathtocheck) - 1] != BDIR)
1202 #endif  
1203                 strcat(pathtocheck, DIRSTRING);
1204             strcat(pathtocheck, path);
1205         }
1206     }
1207     next_path(pathtocheck);
1208
1209     /* Go on to the next level down the path */
1210     while (nextpath = next_path(NULL))
1211     {
1212         strcpy(pathtocheck, nextpath);
1213         if (dflag)
1214             printf("Checking directory [%s]\n", pathtocheck);
1215         /*
1216         * If this is an afs mountpoint, determine what cell from
1217         * the mountpoint name which is of the form
1218         * #cellname:volumename or %cellname:volumename.
1219         */
1220         if (get_afs_mountpoint(pathtocheck, mountpoint, sizeof(mountpoint)))
1221         {
1222             if(dflag)
1223                 printf("Found mount point [%s]\n", mountpoint);
1224             /* skip over the '#' or '%' */
1225             cell = mountpoint + 1;
1226             if (endofcell = strchr(mountpoint, VOLMARKER))
1227             {
1228                 *endofcell = '\0';
1229                 if (auth_to_cell_status = auth_to_cell(context, cell, NULL))
1230                 {
1231                     if (status == AKLOG_SUCCESS)
1232                         status = auth_to_cell_status;
1233                     else if (status != auth_to_cell_status)
1234                         status = AKLOG_SOMETHINGSWRONG;
1235                 }
1236             }
1237         }
1238         else
1239         {
1240             struct stat st;
1241
1242             if (lstat(pathtocheck, &st) < 0)
1243             {
1244                 /*
1245                 * If we've logged and still can't stat, there's
1246                 * a problem...
1247                 */
1248                 fprintf(stderr, "%s: stat(%s): %s\n", progname,
1249                          pathtocheck, strerror(errno));
1250                 return(AKLOG_BADPATH);
1251             }
1252             else if (!S_ISDIR(st.st_mode))
1253             {
1254                 /* Allow only directories */
1255                 fprintf(stderr, "%s: %s: %s\n", progname, pathtocheck,
1256                          strerror(ENOTDIR));
1257                 return(AKLOG_BADPATH);
1258             }
1259         }
1260     }
1261
1262     return(status);
1263 }
1264
1265 /* Print usage message and exit */
1266 static void usage(void)
1267 {
1268     fprintf(stderr, "\nUsage: %s %s%s%s%s\n", progname,
1269              "[-d] [[-cell | -c] cell [-k krb_realm]] ",
1270              "[[-p | -path] pathname]\n",
1271              "    [-noprdb] [-force]\n",
1272 #ifdef HAVE_KRB4
1273              "    [-5 [-m]| -4]\n"
1274 #else
1275              "    [-5]\n"
1276 #endif
1277              );
1278     fprintf(stderr, "    -d gives debugging information.\n");
1279     fprintf(stderr, "    krb_realm is the kerberos realm of a cell.\n");
1280     fprintf(stderr, "    pathname is the name of a directory to which ");
1281     fprintf(stderr, "you wish to authenticate.\n");
1282     fprintf(stderr, "    -noprdb means don't try to determine AFS ID.\n");
1283 #ifdef HAVE_KRB4
1284     fprintf(stderr, "    -5 or -4 selects whether to use Kerberos v5 or Kerberos v4.\n"
1285                     "       (default is Kerberos v5)\n");
1286     fprintf(stderr, "       -m means use krb524d to convert Kerberos v5 tickets.\n");
1287 #else
1288     fprintf(stderr, "    -5 use Kerberos v5.\n"
1289                     "       (only Kerberos v5 is available)\n");
1290 #endif
1291     fprintf(stderr, "    No commandline arguments means ");
1292     fprintf(stderr, "authenticate to the local cell.\n");
1293     fprintf(stderr, "\n");
1294     akexit(AKLOG_USAGE);
1295 }
1296
1297 void
1298 validate_krb5_availability(void)
1299 {
1300 #ifndef _WIN64
1301 #define KRB5LIB "krb5_32.dll"
1302 #else
1303 #define KRB5LIB "krb5_64.dll"
1304 #endif
1305     HINSTANCE h = LoadLibrary(KRB5LIB);
1306     if (h) 
1307         FreeLibrary(h);
1308     else {
1309         fprintf(stderr, "Kerberos for Windows library %s is not available.\n", KRB5LIB);
1310         akexit(AKLOG_KFW_NOT_INSTALLED);
1311     }
1312 }
1313
1314 void
1315 validate_krb4_availability(void)
1316 {
1317 #ifdef HAVE_KRB4
1318     HINSTANCE h = LoadLibrary("krbv4w32.dll");
1319     if (h) 
1320         FreeLibrary(h);
1321     else {
1322         fprintf(stderr, "Kerberos for Windows library krbv4w32.dll is not available.\n");
1323         akexit(AKLOG_KFW_NOT_INSTALLED);
1324     }
1325 #else
1326     fprintf(stderr, "Kerberos v4 is not available in this build of aklog.\n");
1327     akexit(AKLOG_USAGE);
1328 #endif
1329 }
1330
1331 int main(int argc, char *argv[])
1332 {
1333     int status = AKLOG_SUCCESS;
1334     int i;
1335     int somethingswrong = FALSE;
1336
1337     cellinfo_t cellinfo;
1338
1339     extern char *progname;      /* Name of this program */
1340
1341     extern int dflag;           /* Debug mode */
1342
1343     int cmode = FALSE;          /* Cellname mode */
1344     int pmode = FALSE;          /* Path name mode */
1345
1346     char realm[REALM_SZ];               /* Kerberos realm of afs server */
1347     char cell[BUFSIZ];          /* Cell to which we are authenticating */
1348     char path[MAXPATHLEN + 1];  /* Path length for path mode */
1349
1350     linked_list cells;          /* List of cells to log to */
1351     linked_list paths;          /* List of paths to log to */
1352     ll_node *cur_node;
1353
1354     memset(&cellinfo, 0, sizeof(cellinfo));
1355
1356     memset(realm, 0, sizeof(realm));
1357     memset(cell, 0, sizeof(cell));
1358     memset(path, 0, sizeof(path));
1359
1360     ll_init(&cells);
1361     ll_init(&paths);
1362
1363     /* Store the program name here for error messages */
1364     if (progname = LastComponent(argv[0]))
1365         progname++;
1366     else
1367         progname = argv[0];
1368
1369     /* Initialize list of cells to which we have authenticated */
1370     (void)ll_init(&authedcells);
1371
1372     /* Parse commandline arguments and make list of what to do. */
1373     for (i = 1; i < argc; i++)
1374     {
1375         if (strcmp(argv[i], "-d") == 0)
1376             dflag++;
1377         else if (strcmp(argv[i], "-5") == 0)
1378             usev5++;
1379 #ifdef HAVE_KRB4
1380         else if (strcmp(argv[i], "-m") == 0)
1381             use524++;
1382         else if (strcmp(argv[i], "-4") == 0)
1383             usev5 = 0;
1384 #endif
1385         else if (strcmp(argv[i], "-noprdb") == 0)
1386             noprdb++;
1387         else if (strcmp(argv[i], "-force") == 0)
1388             force++;
1389         else if (((strcmp(argv[i], "-cell") == 0) ||
1390                    (strcmp(argv[i], "-c") == 0)) && !pmode)
1391         {       
1392             if (++i < argc)
1393             {
1394                 cmode++;
1395                 strcpy(cell, argv[i]);
1396             }
1397             else
1398                 usage();
1399         }
1400         else if (((strcmp(argv[i], "-path") == 0) ||
1401                    (strcmp(argv[i], "-p") == 0)) && !cmode)
1402         {       
1403             if (++i < argc)
1404             {
1405                 pmode++;
1406                 strcpy(path, argv[i]);
1407             }
1408             else
1409                 usage();
1410         }
1411         else if (argv[i][0] == '-')
1412             usage();
1413         else if (!pmode && !cmode)
1414         {
1415             if (FirstComponent(argv[i]) || (strcmp(argv[i], ".") == 0) ||
1416                  (strcmp(argv[i], "..") == 0))
1417             {
1418                 pmode++;
1419                 strcpy(path, argv[i]);
1420             }
1421             else
1422             {
1423                 cmode++;
1424                 strcpy(cell, argv[i]);
1425             }
1426         }
1427         else
1428             usage();
1429
1430         if (cmode)
1431         {
1432             if (((i + 1) < argc) && (strcmp(argv[i + 1], "-k") == 0))
1433             {
1434                 i += 2;
1435                 if (i < argc)
1436                     strcpy(realm, argv[i]);
1437                 else
1438                     usage();
1439             }
1440             /* Add this cell to list of cells */
1441             strcpy(cellinfo.cell, cell);
1442             strcpy(cellinfo.realm, realm);
1443             if (cur_node = ll_add_node(&cells, ll_tail))
1444             {
1445                 char *new_cellinfo;
1446                 if (new_cellinfo = copy_cellinfo(&cellinfo))
1447                     ll_add_data(cur_node, new_cellinfo);
1448                 else
1449                 {
1450                     fprintf(stderr, "%s: failure copying cellinfo.\n", progname);
1451                     akexit(AKLOG_MISC);
1452                 }
1453             }
1454             else
1455             {
1456                 fprintf(stderr, "%s: failure adding cell to cells list.\n",
1457                          progname);
1458                 akexit(AKLOG_MISC);
1459             }
1460             memset(&cellinfo, 0, sizeof(cellinfo));
1461             cmode = FALSE;
1462             memset(cell, 0, sizeof(cell));
1463             memset(realm, 0, sizeof(realm));
1464         }
1465         else if (pmode)
1466         {
1467             /* Add this path to list of paths */
1468             if (cur_node = ll_add_node(&paths, ll_tail))
1469             {
1470                 char *new_path;
1471                 if (new_path = strdup(path))
1472                     ll_add_data(cur_node, new_path);
1473                 else
1474                 {
1475                     fprintf(stderr, "%s: failure copying path name.\n",
1476                              progname);
1477                     akexit(AKLOG_MISC);
1478                 }
1479             }
1480             else
1481             {
1482                 fprintf(stderr, "%s: failure adding path to paths list.\n",
1483                          progname);
1484                 akexit(AKLOG_MISC);
1485             }
1486             pmode = FALSE;
1487             memset(path, 0, sizeof(path));
1488         }
1489     }
1490
1491     if (usev5) {
1492         validate_krb5_availability();
1493         if (krb5_init_context(&context))
1494             return(AKLOG_KERBEROS);
1495     } else 
1496         validate_krb4_availability();
1497
1498
1499     /* If nothing was given, log to the local cell. */
1500     if ((cells.nelements + paths.nelements) == 0)
1501         status = auth_to_cell(context, NULL, NULL);
1502     else
1503     {
1504         /* Log to all cells in the cells list first */
1505         for (cur_node = cells.first; cur_node; cur_node = cur_node->next)
1506         {
1507             memcpy(&cellinfo, cur_node->data, sizeof(cellinfo));
1508             if (status = auth_to_cell(context, 
1509                                        cellinfo.cell, cellinfo.realm))
1510                 somethingswrong++;
1511         }       
1512
1513         /* Then, log to all paths in the paths list */
1514         for (cur_node = paths.first; cur_node; cur_node = cur_node->next)
1515         {
1516             if (status = auth_to_path(context, 
1517                                        cur_node->data))
1518                 somethingswrong++;
1519         }
1520
1521         /*
1522         * If only one thing was logged to, we'll return the status
1523         * of the single call.  Otherwise, we'll return a generic
1524         * something failed status.
1525         */
1526         if (somethingswrong && ((cells.nelements + paths.nelements) > 1))
1527             status = AKLOG_SOMETHINGSWRONG;
1528     }       
1529
1530     akexit(status);
1531 }