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