aklog: Tidy realm handling
[openafs.git] / src / aklog / aklog.c
1 /* 
2  * $Id$
3  *
4  * Copyright 1990,1991 by the Massachusetts Institute of Technology
5  * For distribution and copying rights, see the file "mit-copyright.h"
6  */
7 /*
8  * Copyright (c) 2005, 2006                                     
9  * The Linux Box Corporation                                    
10  * ALL RIGHTS RESERVED                                          
11  *                                                              
12  * Permission is granted to use, copy, create derivative works  
13  * and redistribute this software and such derivative works     
14  * for any purpose, so long as the name of the Linux Box        
15  * Corporation is not used in any advertising or publicity      
16  * pertaining to the use or distribution of this software       
17  * without specific, written prior authorization.  If the       
18  * above copyright notice or any other identification of the    
19  * Linux Box Corporation is included in any copy of any         
20  * portion of this software, then the disclaimer below must     
21  * also be included.                                            
22  *                                                              
23  * This software is provided as is, without representation      
24  * from the Linux Box Corporation as to its fitness for any     
25  * purpose, and without warranty by the Linux Box Corporation   
26  * of any kind, either express or implied, including            
27  * without limitation the implied warranties of                 
28  * merchantability and fitness for a particular purpose.  The   
29  * regents of the Linux Box Corporation shall not be liable     
30  * for any damages, including special, indirect, incidental, or 
31  * consequential damages, with respect to any claim arising     
32  * out of or in connection with the use of the software, even   
33  * if it has been or is hereafter advised of the possibility of 
34  * such damages.                                                
35  */
36
37 #include <afsconfig.h>
38
39 #include <stdio.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <sys/types.h>
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46 #ifdef HAVE_STDLIB_H
47 #include <stdlib.h>
48 #endif
49 #ifdef HAVE_MEMORY_H
50 #include <memory.h>
51 #endif
52 #include <errno.h>
53
54 #include <sys/stat.h>
55 #include <fcntl.h>
56
57 #include <sys/param.h>
58 #include <sys/errno.h>
59 #include <netdb.h>
60 #include <sys/socket.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
63 #include <pwd.h>
64
65 #include <afs/stds.h>
66 #include <krb5.h>
67 #if defined(HAVE_ET_COM_ERR_H)
68 #include <et/com_err.h>
69 #else
70 #include <com_err.h>
71 #endif
72
73 #ifndef HAVE_KERBEROSV_HEIM_ERR_H
74 #include <afs/com_err.h>
75 #endif
76
77 #include <afs/param.h>
78 #ifdef AFS_SUN5_ENV
79 #include <sys/ioccom.h>
80 #endif
81
82 /* Prevent inclusion of des.h to avoid conflicts with des types */
83 #define NO_DES_H_INCLUDE
84
85 #include <afs/auth.h>
86 #include <afs/cellconfig.h>
87 #include <afs/vice.h>
88 #include <afs/venus.h>
89 #include <afs/ptserver.h>
90 #include <afs/ptuser.h>
91 #include <afs/dirpath.h>
92
93 #include "aklog.h"
94 #include "linked_list.h"
95
96 #ifdef HAVE_KRB5_CREDS_KEYBLOCK
97 #define USING_MIT 1
98 #endif
99 #ifdef HAVE_KRB5_CREDS_SESSION
100 #define USING_HEIMDAL 1
101 #endif
102
103 #define AFSKEY "afs"
104 #define AFSINST ""
105
106 #ifndef AFS_TRY_FULL_PRINC
107 #define AFS_TRY_FULL_PRINC 1
108 #endif /* AFS_TRY_FULL_PRINC */
109
110 #define AKLOG_TRYAGAIN -1
111 #define AKLOG_SUCCESS 0
112 #define AKLOG_USAGE 1
113 #define AKLOG_SOMETHINGSWRONG 2
114 #define AKLOG_AFS 3
115 #define AKLOG_KERBEROS 4
116 #define AKLOG_TOKEN 5
117 #define AKLOG_BADPATH 6
118 #define AKLOG_MISC 7
119
120 #ifndef NULL
121 #define NULL 0
122 #endif
123
124 #ifndef TRUE
125 #define TRUE 1
126 #endif
127
128 #ifndef FALSE
129 #define FALSE 0
130 #endif
131
132 #ifndef MAXSYMLINKS
133 /* RedHat 4.x doesn't seem to define this */
134 #define MAXSYMLINKS     5
135 #endif
136
137 #define DIR '/'                 /* Character that divides directories */
138 #define DIRSTRING "/"           /* String form of above */
139 #define VOLMARKER ':'           /* Character separating cellname from mntpt */
140 #define VOLMARKERSTRING ":"     /* String form of above */
141
142 typedef struct {
143     char cell[BUFSIZ];
144     char realm[REALM_SZ];
145 } cellinfo_t;
146
147 static krb5_ccache  _krb425_ccache = NULL;
148
149 /*
150  * Why doesn't AFS provide these prototypes?
151  */
152
153 extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32);
154
155 /*
156  * Other prototypes
157  */
158
159 extern char *afs_realm_of_cell(krb5_context, struct afsconf_cell *, int);
160 static int isdir(char *, unsigned char *);
161 static krb5_error_code get_credv5(krb5_context context, char *, char *,
162                                   char *, krb5_creds **);
163 static int get_user_realm(krb5_context, char **);
164
165 #define TRYAGAIN(x) (x == AKLOG_TRYAGAIN || \
166                      x == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || \
167                      x == KRB5KRB_ERR_GENERIC)
168
169 #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
170
171 #define get_princ_str(c, p, n) krb5_princ_component(c, p, n)->data
172 #define get_princ_len(c, p, n) krb5_princ_component(c, p, n)->length
173 #define second_comp(c, p) (krb5_princ_size(c, p) > 1)
174 #define realm_data(c, p) krb5_princ_realm(c, p)->data
175 #define realm_len(c, p) krb5_princ_realm(c, p)->length
176
177 #elif defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
178
179 #define get_princ_str(c, p, n) krb5_principal_get_comp_string(c, p, n)
180 #define get_princ_len(c, p, n) strlen(krb5_principal_get_comp_string(c, p, n))
181 #define second_comp(c, p) (krb5_principal_get_comp_string(c, p, 1) != NULL)
182 #define realm_data(c, p) krb5_realm_data(krb5_principal_get_realm(c, p))
183 #define realm_len(c, p) krb5_realm_length(krb5_principal_get_realm(c, p))
184
185 #else
186 #error "Must have either krb5_princ_size or krb5_principal_get_comp_string"
187 #endif
188
189 #if !defined(HAVE_KRB5_ENCRYPT_TKT_PART) && defined(HAVE_ENCODE_KRB5_ENC_TKT_PART) && defined(HAVE_KRB5_C_ENCRYPT) 
190 extern krb5_error_code encode_krb5_enc_tkt_part (const krb5_enc_tkt_part *rep,
191                                                  krb5_data **code);
192
193 krb5_error_code
194 krb5_encrypt_tkt_part(krb5_context context,
195                       const krb5_keyblock *key,
196                       krb5_ticket *ticket)
197 {
198     krb5_data *data = 0;
199     int code;
200     size_t enclen;
201     
202     if ((code = encode_krb5_enc_tkt_part(ticket->enc_part2, &data)))
203         goto Done;
204     if ((code = krb5_c_encrypt_length(context, key->enctype,
205                                       data->length, &enclen)))
206         goto Done;
207     ticket->enc_part.ciphertext.length = enclen;
208     if (!(ticket->enc_part.ciphertext.data = malloc(enclen))) {
209         code = ENOMEM;
210         goto Done;
211     }
212     if ((code = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_KDC_REP_TICKET,
213                                0, data, &ticket->enc_part))) {
214         free(ticket->enc_part.ciphertext.data);
215         ticket->enc_part.ciphertext.data = 0;
216     }
217 Done:
218     if (data) {
219         if (data->data)
220             free(data->data);
221         free(data);
222     }
223     return code;
224 }
225 #endif
226
227 #if defined(HAVE_KRB5_CREDS_KEYBLOCK)
228
229 #define get_cred_keydata(c) c->keyblock.contents
230 #define get_cred_keylen(c) c->keyblock.length
231 #define get_creds_enctype(c) c->keyblock.enctype
232
233 #elif defined(HAVE_KRB5_CREDS_SESSION)
234
235 #define get_cred_keydata(c) c->session.keyvalue.data
236 #define get_cred_keylen(c) c->session.keyvalue.length
237 #define get_creds_enctype(c) c->session.keytype
238
239 #else
240 #error "Must have either keyblock or session member of krb5_creds"
241 #endif
242
243 #if !defined(HAVE_KRB5_524_CONVERT_CREDS) && defined(HAVE_KRB524_CONVERT_CREDS_KDC)
244 #define krb5_524_convert_creds krb524_convert_creds_kdc
245 #elif !defined(HAVE_KRB5_524_CONVERT_CREDS) && !defined(HAVE_KRB524_CONVERT_CREDS_KDC)
246 #define HAVE_NO_KRB5_524
247 #endif
248
249 #if USING_HEIMDAL
250 #define deref_keyblock_enctype(kb)              \
251     ((kb)->keytype)
252
253 #define deref_entry_keyblock(entry)             \
254     entry->keyblock
255
256 #define deref_session_key(creds)                \
257     creds->session
258
259 #define deref_enc_tkt_addrs(tkt)                \
260     tkt->caddr
261
262 #define deref_enc_length(enc)                   \
263     ((enc)->cipher.length)
264
265 #define deref_enc_data(enc)                     \
266     ((enc)->cipher.data)
267
268 #define krb5_free_keytab_entry_contents krb5_kt_free_entry
269
270 #else
271 #define deref_keyblock_enctype(kb)              \
272     ((kb)->enctype)
273
274 #define deref_entry_keyblock(entry)             \
275     entry->key
276
277 #define deref_session_key(creds)                \
278     creds->keyblock
279
280 #define deref_enc_tkt_addrs(tkt)                \
281     tkt->caddrs
282
283 #define deref_enc_length(enc)                   \
284     ((enc)->ciphertext.length)
285
286 #define deref_enc_data(enc)                     \
287     ((enc)->ciphertext.data)
288
289 #endif
290
291 #define deref_entry_enctype(entry)                      \
292     deref_keyblock_enctype(&deref_entry_keyblock(entry))
293
294 /*
295  * Provide a replacement for strerror if we don't have it
296  */
297
298 #ifndef HAVE_STRERROR
299 extern char *sys_errlist[];
300 #define strerror(x) sys_errlist[x]
301 #endif /* HAVE_STRERROR */
302
303 static char *progname = NULL;   /* Name of this program */
304 static int dflag = FALSE;       /* Give debugging information */
305 static int noauth = FALSE;      /* If true, don't try to get tokens */
306 static int zsubs = FALSE;       /* Are we keeping track of zephyr subs? */
307 static int hosts = FALSE;       /* Are we keeping track of hosts? */
308 static int noprdb = FALSE;      /* Skip resolving name to id? */
309 static int linked = FALSE;      /* try for both AFS nodes */
310 static int afssetpag = FALSE;   /* setpag for AFS */
311 static int force = FALSE;       /* Bash identical tokens? */
312 static int do524 = FALSE;       /* Should we do 524 instead of rxkad2b? */
313 static char *keytab = NULL;     /* keytab for akimpersonate */
314 static char *client = NULL;     /* client principal for akimpersonate */
315 static linked_list zsublist;    /* List of zephyr subscriptions */
316 static linked_list hostlist;    /* List of host addresses */
317 static linked_list authedcells; /* List of cells already logged to */
318
319 /* A com_error bodge. The idea here is that this routine lets us lookup
320  * things in the system com_err, if the AFS one just tells us the error
321  * is unknown
322  */
323
324 void
325 redirect_errors(const char *who, afs_int32 code, const char *fmt, va_list ap)
326 {
327     if (who) {
328         fputs(who, stderr);
329         fputs(": ", stderr);
330     }
331     if (code) {
332         const char *str = afs_error_message(code);
333         if (strncmp(str, "unknown", strlen("unknown")) == 0) {
334             str = error_message(code);
335         }
336         fputs(str, stderr);
337         fputs(" ", stderr);
338     }
339     if (fmt) {
340         vfprintf(stderr, fmt, ap);
341     }
342     putc('\n', stderr);
343     fflush(stderr);
344 }
345
346 static char *
347 copy_cellinfo(cellinfo_t *cellinfo)
348 {
349     cellinfo_t *new_cellinfo;
350
351     if ((new_cellinfo = (cellinfo_t *)malloc(sizeof(cellinfo_t))))
352         memcpy(new_cellinfo, cellinfo, sizeof(cellinfo_t));
353     
354     return ((char *)new_cellinfo);
355 }
356
357
358 static int
359 get_cellconfig(char *cell, struct afsconf_cell *cellconfig, char **local_cell)
360 {
361     int status = AKLOG_SUCCESS;
362     struct afsconf_dir *configdir;
363
364     memset(cellconfig, 0, sizeof(*cellconfig));
365
366     *local_cell = malloc(MAXCELLCHARS);
367     if (*local_cell == NULL) {
368         fprintf(stderr, "%s: can't allocate memory for local cell name\n",
369                 progname);
370         exit(AKLOG_AFS);
371     }
372
373     if (!(configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))) {
374         fprintf(stderr, 
375                 "%s: can't get afs configuration (afsconf_Open(%s))\n",
376                 progname, AFSDIR_CLIENT_ETC_DIRPATH);
377         exit(AKLOG_AFS);
378     }
379
380     if (afsconf_GetLocalCell(configdir, *local_cell, MAXCELLCHARS)) {
381         fprintf(stderr, "%s: can't determine local cell.\n", progname);
382         exit(AKLOG_AFS);
383     }
384
385     if ((cell == NULL) || (cell[0] == 0))
386         cell = *local_cell;
387
388     /* XXX - This function modifies 'cell' by passing it through lcstring */
389     if (afsconf_GetCellInfo(configdir, cell, NULL, cellconfig)) {
390         fprintf(stderr, "%s: Can't get information about cell %s.\n",
391                 progname, cell);
392         status = AKLOG_AFS;
393     }
394
395     afsconf_Close(configdir);
396
397     return(status);
398 }
399
400 static char *
401 extract_realm(krb5_context context, krb5_principal princ) {
402     int len;
403     char *realm;
404
405     len = realm_len(context, princ);
406     if (len > REALM_SZ-1)
407         len = REALM_SZ-1;
408
409     realm = malloc(sizeof(char) * (len+1));
410     if (realm == NULL)
411         return NULL;
412
413     strncpy(realm, realm_data(context, princ), len);
414     realm[len] = '\0';
415
416     return realm;
417 }
418
419 static int
420 get_realm_from_cred(krb5_context context, krb5_creds *v5cred, char **realm) {
421 #if !defined(HEIMDAL) && defined(HAVE_KRB5_DECODE_TICKET)
422     krb5_error_code code;
423     krb5_ticket *ticket;
424
425     *realm = NULL;
426
427     code = krb5_decode_ticket(&v5cred->ticket, &ticket);
428     if (code)
429         return code;
430
431     *realm = extract_realm(context, ticket->server);
432     if (*realm == NULL)
433         code = ENOMEM;
434
435     krb5_free_ticket(context, ticket);
436
437     return code;
438 #else
439     *realm = NULL;
440     return 0;
441 #endif
442 }
443
444 /* 
445  * Log to a cell.  If the cell has already been logged to, return without
446  * doing anything.  Otherwise, log to it and mark that it has been logged
447  * to.
448  */
449 static int
450 auth_to_cell(krb5_context context, char *cell, char *realm, char **linkedcell)
451 {
452     int status = AKLOG_SUCCESS;
453     char username[BUFSIZ];      /* To hold client username structure */
454     afs_int32 viceId;           /* AFS uid of user */
455
456     char *realm_of_user = NULL;    /* Kerberos realm of user */
457     char *realm_from_princ = NULL; /* Calculated realm data */
458     char *realm_of_cell = NULL;    /* Pointer to realm we're using */
459     int retry;                     /* round, and round we go ... */
460     
461     char *local_cell = NULL;
462     krb5_creds *v5cred = NULL;
463     struct ktc_principal aserver;
464     struct ktc_principal aclient;
465     struct ktc_token atoken, btoken;
466     struct afsconf_cell cellconf;
467
468     /* NULL or empty cell returns information on local cell */
469     if ((status = get_cellconfig(cell, &cellconf, &local_cell)))
470         return(status);
471
472     if (linkedcell != NULL && cellconf.linkedCell != NULL) {
473         *linkedcell = strdup(cellconf.linkedCell);
474         if (*linkedcell == NULL) {
475             status = ENOMEM;
476             goto out;
477         }
478     }
479
480     if (ll_string(&authedcells, ll_s_check, cellconf.name)) {
481         if (dflag) {
482             printf("Already authenticated to %s (or tried to)\n", 
483                    cellconf.name);
484         }
485         status = AKLOG_SUCCESS;
486         goto out;
487     }
488
489     /* 
490      * Record that we have attempted to log to this cell.  We do this
491      * before we try rather than after so that we will not try
492      * and fail repeatedly for one cell.
493      */
494     ll_string(&authedcells, ll_s_add, cellconf.name);
495
496     /* 
497      * Record this cell in the list of zephyr subscriptions.  We may
498      * want zephyr subscriptions even if authentication fails.
499      * If this is done after we attempt to get tokens, aklog -zsubs
500      * can return something different depending on whether or not we
501      * are in -noauth mode.
502      */
503     if (ll_string(&zsublist, ll_s_add, cellconf.name) == LL_FAILURE) {
504         fprintf(stderr, 
505                 "%s: failure adding cell %s to zephyr subscriptions list.\n",
506                 progname, cellconf.name);
507         exit(AKLOG_MISC);
508     }
509     if (ll_string(&zsublist, ll_s_add, local_cell) == LL_FAILURE) {
510         fprintf(stderr, 
511                 "%s: failure adding cell %s to zephyr subscriptions list.\n",
512                 progname, local_cell);
513         exit(AKLOG_MISC);
514     }
515
516     if (!noauth) {
517         if (dflag) {
518             printf("Authenticating to cell %s (server %s).\n",
519                    cellconf.name, cellconf.hostName[0]);
520         }
521
522         if ((status = get_user_realm(context, &realm_of_user))) {
523             fprintf(stderr, "%s: Couldn't determine realm of user:)",
524                     progname);
525             afs_com_err(progname, status, " while getting realm");
526             status = AKLOG_KERBEROS;
527             goto out;
528         }
529
530         retry = 1;
531         
532         while(retry) {
533
534             /* This code tries principals in the following, much debated,
535              * order:
536              * 
537              * If the realm is specified on the command line we do
538              *    - afs/cell@COMMAND-LINE-REALM
539              *    - afs@COMMAND-LINE-REALM
540              * 
541              * Otherwise, we do
542              *    - afs/cell@REALM-FROM-USERS-PRINCIPAL
543              *    - afs/cell@krb5_get_host_realm(db-server)
544              *   Then, if krb5_get_host_realm(db-server) is non-empty
545              *      - afs@ krb5_get_host_realm(db-server)
546              *   Otherwise
547              *      - afs/cell@ upper-case-domain-of-db-server
548              *      - afs@ upper-case-domain-of-db-server
549              * 
550              * In all cases, the 'afs@' variant is only tried where the
551              * cell and the realm match case-insensitively.
552              */
553                 
554             /* Cell on command line - use that one */
555             if (realm && realm[0]) {
556                 realm_of_cell = realm;
557                 status = AKLOG_TRYAGAIN;
558                 if (dflag) {
559                     printf("We were told to authenticate to realm %s.\n", 
560                            realm);
561                 }
562             } else {
563                 /* Initially, try using afs/cell@USERREALM */
564                 if (dflag) {
565                     printf("Trying to authenticate to user's realm %s.\n",
566                            realm_of_user);
567                 }
568                 
569                 realm_of_cell = realm_of_user;
570                 status = get_credv5(context, AFSKEY, cellconf.name,
571                                     realm_of_cell, &v5cred);
572             
573                 /* If that failed, try to determine the realm from the name of 
574                  * one of the DB servers */
575                 if (TRYAGAIN(status)) {
576                     realm_of_cell = afs_realm_of_cell(context, &cellconf,
577                                                       FALSE);
578                     if (!realm_of_cell) {
579                         fprintf(stderr, 
580                                 "%s: Couldn't figure out realm for cell %s.\n",
581                                 progname, cellconf.name);
582                         exit(AKLOG_MISC);
583                     }
584
585                     if (dflag) {
586                         if (realm_of_cell[0])
587                             printf("We've deduced that we need to authenticate"
588                                    " to realm %s.\n", realm_of_cell);
589                     else
590                         printf("We've deduced that we need to authenticate "
591                                "using referrals.\n");
592                     }
593                 }
594             }
595         
596             if (TRYAGAIN(status)) {
597                 /* If we've got the full-princ-first option, or we're in a
598                  * different realm from the cell - use the cell name as the
599                  * instance */
600                 if (AFS_TRY_FULL_PRINC || 
601                     strcasecmp(cellconf.name, realm_of_cell)!=0) {
602                     status = get_credv5(context, AFSKEY, cellconf.name,
603                                         realm_of_cell, &v5cred);
604
605                     /* If we failed & we've got an empty realm, then try 
606                      * calling afs_realm_for_cell again. */
607                     if (TRYAGAIN(status) && !realm_of_cell[0]) {
608                         /* This time, get the realm by taking the domain 
609                          * component of the db server and make it upper case */
610                         realm_of_cell = afs_realm_of_cell(context,
611                                                           &cellconf, TRUE);
612                         if (!realm_of_cell) {
613                             fprintf(stderr,
614                                     "%s: Couldn't figure out realm for cell "
615                                     "%s.\n", progname, cellconf.name);
616                             exit(AKLOG_MISC);
617                         }
618                         if (dflag) {
619                             printf("We've deduced that we need to authenticate"
620                                    " to realm %s.\n", realm_of_cell);
621                         }
622                     }
623                     status = get_credv5(context, AFSKEY, cellconf.name,
624                                         realm_of_cell, &v5cred);
625                 }
626            
627                 /* If the realm and cell name match, then try without an 
628                  * instance, but only if realm is non-empty */
629                 
630                 if (TRYAGAIN(status) && 
631                     strcasecmp(cellconf.name, realm_of_cell) == 0) {
632                     status = get_credv5(context, AFSKEY, NULL, 
633                                         realm_of_cell, &v5cred);
634                     if (!AFS_TRY_FULL_PRINC && TRYAGAIN(status)) {
635                         status = get_credv5(context, AFSKEY, cellconf.name,
636                                             realm_of_cell, &v5cred);
637                     }
638                 }
639             }
640
641             /* Try to find a service principal for this cell.
642              * Some broken MIT libraries return KRB5KRB_AP_ERR_MSG_TYPE upon 
643              * the first attempt, so we try twice to be sure */
644
645             if (status == KRB5KRB_AP_ERR_MSG_TYPE && retry == 1)
646                 retry++;
647             else
648                 retry = 0;
649         } 
650         
651         if (status != 0) {
652             if (dflag) {
653                 printf("Kerberos error code returned by get_cred : %d\n",
654                        status);
655             }
656             fprintf(stderr, "%s: Couldn't get %s AFS tickets:\n",
657                     progname, cellconf.name);
658             afs_com_err(progname, status, "while getting AFS tickets");
659             status = AKLOG_KERBEROS;
660             goto out;
661         }
662         
663         /* If we've got a valid ticket, and we still don't know the realm name
664          * try to figure it out from the contents of the ticket
665          */
666 #if !defined(USING_HEIMDAL) && defined(HAVE_KRB5_DECODE_TICKET)
667         if (strcmp(realm_of_cell, "") == 0) {
668             krb5_error_code code;
669             krb5_ticket *ticket;
670
671             code = krb5_decode_ticket(&v5cred->ticket, &ticket);
672
673             if (code != 0) {
674                 fprintf(stderr,
675                         "%s: Couldn't decode ticket to determine realm for "
676                         "cell %s.\n",
677                         progname, cellconf.name);
678             } else {
679                 realm_from_princ = extract_realm(context, ticket->server);
680                 realm_of_cell = realm_from_princ;
681                 
682                 krb5_free_ticket(context, ticket);
683             }
684         }
685 #endif
686
687         strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
688         strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
689         strncpy(aserver.cell, cellconf.name, MAXKTCREALMLEN - 1);
690
691         /*
692          * The default is to use rxkad2b, which means we put in a full
693          * V5 ticket.  If the user specifies -524, we talk to the
694          * 524 ticket converter.
695          */
696
697         if (! do524) {
698             char k4name[ANAME_SZ], k4inst[INST_SZ], k4realm[REALM_SZ];
699 #ifdef HAVE_NO_KRB5_524
700             char *p;
701             int len;
702 #endif
703
704             if (dflag)
705                 printf("Using Kerberos V5 ticket natively\n");
706
707 #ifndef HAVE_NO_KRB5_524
708             status = krb5_524_conv_principal (context, v5cred->client,
709                                               (char *) &k4name,
710                                               (char *) &k4inst,
711                                               (char *) &k4realm);
712             if (status) {
713                 afs_com_err(progname, status, "while converting principal "
714                         "to Kerberos V4 format");
715                 status = AKLOG_KERBEROS;
716                 goto out;
717             }
718             strcpy (username, k4name);
719             if (k4inst[0]) {
720                 strcat (username, ".");
721                 strcat (username, k4inst);
722             }
723 #else
724             len = min(get_princ_len(context, v5cred->client, 0),
725                       second_comp(context, v5cred->client) ?
726                       MAXKTCNAMELEN - 2 : MAXKTCNAMELEN - 1);
727             strncpy(username, get_princ_str(context, v5cred->client, 0), len);
728             username[len] = '\0';
729             
730             if (second_comp(context, v5cred->client)) {
731                 strcat(username, ".");
732                 p = username + strlen(username);
733                 len = min(get_princ_len(context, v5cred->client, 1),
734                           MAXKTCNAMELEN - strlen(username) - 1);
735                 strncpy(p, get_princ_str(context, v5cred->client, 1), len);
736                 p[len] = '\0';
737             }
738 #endif
739
740             memset(&atoken, 0, sizeof(atoken));
741             atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
742             atoken.startTime = v5cred->times.starttime;;
743             atoken.endTime = v5cred->times.endtime;
744             memcpy(&atoken.sessionKey, get_cred_keydata(v5cred),
745                    get_cred_keylen(v5cred));
746             atoken.ticketLen = v5cred->ticket.length;
747             memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen);
748 #ifndef HAVE_NO_KRB5_524
749         } else {
750             CREDENTIALS cred;
751
752             if (dflag)
753                 printf("Using Kerberos 524 translator service\n");
754
755             status = krb5_524_convert_creds(context, v5cred, &cred);
756
757             if (status) {
758                 afs_com_err(progname, status, "while converting tickets "
759                         "to Kerberos V4 format");
760                 status = AKLOG_KERBEROS;
761                 goto out;
762             }
763
764             strcpy (username, cred.pname);
765             if (cred.pinst[0]) {
766                 strcat (username, ".");
767                 strcat (username, cred.pinst);
768             }
769
770             atoken.kvno = cred.kvno;
771             atoken.startTime = cred.issue_date;
772             /*
773              * It seems silly to go through a bunch of contortions to
774              * extract the expiration time, when the v5 credentials already
775              * has the exact time!  Let's use that instead.
776              *
777              * Note that this isn't a security hole, as the expiration time
778              * is also contained in the encrypted token
779              */
780             atoken.endTime = v5cred->times.endtime;
781             memcpy(&atoken.sessionKey, cred.session, 8);
782             atoken.ticketLen = cred.ticket_st.length;
783             memcpy(atoken.ticket, cred.ticket_st.dat, atoken.ticketLen);
784 #endif /* HAVE_NO_KRB5_524 */
785         }
786         
787         if (!force &&
788             !ktc_GetToken(&aserver, &btoken, sizeof(btoken), &aclient) &&
789             atoken.kvno == btoken.kvno &&
790             atoken.ticketLen == btoken.ticketLen &&
791             !memcmp(&atoken.sessionKey, &btoken.sessionKey, sizeof(atoken.sessionKey)) &&
792             !memcmp(atoken.ticket, btoken.ticket, atoken.ticketLen)) {
793
794             if (dflag) {
795                 printf("Identical tokens already exist; skipping.\n");
796             }
797             status = AKLOG_SUCCESS;
798             goto out;
799         }
800
801 #ifdef FORCE_NOPRDB
802         noprdb = 1;
803 #endif
804
805         if (noprdb) {
806             if (dflag) {
807                 printf("Not resolving name %s to id (-noprdb set)\n",
808                         username);
809             }
810         }
811         else {
812             if (strcmp(realm_of_user, realm_of_cell)) {
813                 strcat(username, "@");
814                 strcat(username, realm_of_user);
815             }
816
817             if (dflag) {
818                 printf("About to resolve name %s to id in cell %s.\n", 
819                         username, aserver.cell);
820             }
821
822             if (!pr_Initialize (0,  AFSDIR_CLIENT_ETC_DIRPATH, aserver.cell))
823                 status = pr_SNameToId (username, &viceId);
824             
825             if (dflag) {
826                 if (status) 
827                     printf("Error %d\n", status);
828                 else
829                     printf("Id %d\n", (int) viceId);
830             }
831             
832
833             /*
834              * This code is taken from cklog -- it lets people
835              * automatically register with the ptserver in foreign cells
836              */
837
838 #ifdef ALLOW_REGISTER
839             if ((status == 0) && (viceId == ANONYMOUSID) &&
840                 (strcmp(realm_of_user, realm_of_cell) != 0)) {
841                 if (dflag) {
842                     printf("doing first-time registration of %s "
843                             "at %s\n", username, cellconf.name);
844                 }
845                 viceId = 0;
846                 strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
847                 strcpy(aclient.instance, "");
848                 strncpy(aclient.cell, realm_of_user, MAXKTCREALMLEN - 1);
849                 if ((status = ktc_SetToken(&aserver, &atoken, &aclient, 0))) {
850                     afs_com_err(progname, status,
851                                 "while obtaining tokens for cell %s",
852                                 cellconf.name);
853                     status = AKLOG_TOKEN;
854                 }
855
856                 /*
857                  * In case you're wondering, we don't need to change the
858                  * filename here because we're still connecting to the
859                  * same cell -- we're just using a different authentication
860                  * level
861                  */
862
863                 if ((status = pr_Initialize(1L,  AFSDIR_CLIENT_ETC_DIRPATH,
864                                             aserver.cell))) {
865                     printf("Error %d\n", status);
866                 }
867
868                 if ((status = pr_CreateUser(username, &viceId))) {
869                     fprintf(stderr, "%s: %s so unable to create remote PTS "
870                             "user %s in cell %s (status: %d).\n", progname,
871                             afs_error_message(status), username, cellconf.name,
872                             status);
873                     viceId = ANONYMOUSID;
874                 } else {
875                     printf("created cross-cell entry for %s (Id %d) at %s\n",
876                            username, viceId, cellconf.name);
877                 }
878             }
879 #endif /* ALLOW_REGISTER */
880
881             /*
882              * This is a crock, but it is Transarc's crock, so we have to play
883              * along in order to get the functionality.  The way the afs id is
884              * stored is as a string in the username field of the token.
885              * Contrary to what you may think by looking at the code for
886              * tokens, this hack (AFS ID %d) will not work if you change %d
887              * to something else.
888              */
889
890             if ((status == 0) && (viceId != ANONYMOUSID)) {
891                 sprintf(username, "AFS ID %d", (int) viceId);
892             }
893         }
894
895         if (dflag) {
896             fprintf(stdout, "Set username to %s\n", username);
897         }
898
899         /* Reset the "aclient" structure before we call ktc_SetToken.
900          * This structure was first set by the ktc_GetToken call when
901          * we were comparing whether identical tokens already existed.
902          */
903         strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
904         strcpy(aclient.instance, "");
905         strncpy(aclient.cell, realm_of_user, MAXKTCREALMLEN - 1);
906
907         if (dflag) {
908             printf("Setting tokens. %s / %s @ %s \n",
909                     aclient.name, aclient.instance, aclient.cell );
910         }
911 #ifndef AFS_AIX51_ENV
912         /* on AIX 4.1.4 with AFS 3.4a+ if a write is not done before 
913          * this routine, it will not add the token. It is not clear what 
914          * is going on here! So we will do the following operation.
915          * On AIX 5, it causes the parent program to die, so we won't.
916          */
917         write(2,"",0); /* dummy write */
918 #endif
919         if ((status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag))) {
920             afs_com_err(progname, status, "while obtaining tokens for cell %s",
921                         cellconf.name);
922             status = AKLOG_TOKEN;
923         }
924     }
925     else
926         if (dflag) {
927             printf("Noauth mode; not authenticating.\n");
928         }
929
930 out:
931     if (local_cell)
932         free(local_cell);
933     if (realm_from_princ)
934         free(realm_from_princ);
935     if (realm_of_user)
936         free(realm_of_user);
937
938     return(status);
939 }
940
941 static int
942 get_afs_mountpoint(char *file, char *mountpoint, int size)
943 {
944 #ifdef AFS_SUN_ENV
945     char V ='V'; /* AFS has problem on Sun with pioctl */
946 #endif
947     char our_file[MAXPATHLEN + 1];
948     char *parent_dir;
949     char *last_component;
950     struct ViceIoctl vio;
951     char cellname[BUFSIZ];
952
953     memset(our_file, 0, sizeof(our_file));
954     strcpy(our_file, file);
955
956     if ((last_component = strrchr(our_file, DIR))) {
957         *last_component++ = 0;
958         parent_dir = our_file;
959     }
960     else {
961         last_component = our_file;
962         parent_dir = ".";
963     }    
964     
965     memset(cellname, 0, sizeof(cellname));
966
967     vio.in = last_component;
968     vio.in_size = strlen(last_component)+1;
969     vio.out_size = size;
970     vio.out = mountpoint;
971
972     if (!pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, &vio, 0)) {
973         if (strchr(mountpoint, VOLMARKER) == NULL) {
974             vio.in = file;
975             vio.in_size = strlen(file) + 1;
976             vio.out_size = sizeof(cellname);
977             vio.out = cellname;
978             
979             if (!pioctl(file, VIOC_FILE_CELL_NAME, &vio, 1)) {
980                 strcat(cellname, VOLMARKERSTRING);
981                 strcat(cellname, mountpoint + 1);
982                 memset(mountpoint + 1, 0, size - 1);
983                 strcpy(mountpoint + 1, cellname);
984             }
985         }
986         return(TRUE);
987     }
988     else
989         return(FALSE);
990 }
991
992 /* 
993  * This routine each time it is called returns the next directory 
994  * down a pathname.  It resolves all symbolic links.  The first time
995  * it is called, it should be called with the name of the path
996  * to be descended.  After that, it should be called with the arguemnt
997  * NULL.
998  */
999 static char *
1000 next_path(char *origpath)
1001 {
1002     static char path[MAXPATHLEN + 1];
1003     static char pathtocheck[MAXPATHLEN + 1];
1004
1005     int link = FALSE;           /* Is this a symbolic link? */
1006     char linkbuf[MAXPATHLEN + 1];
1007     char tmpbuf[MAXPATHLEN + 1];
1008
1009     static char *last_comp;     /* last component of directory name */
1010     static char *elast_comp;    /* End of last component */
1011     char *t;
1012     int len;
1013     
1014     static int symlinkcount = 0; /* We can't exceed MAXSYMLINKS */
1015     
1016     /* If we are given something for origpath, we are initializing only. */
1017     if (origpath) {
1018         memset(path, 0, sizeof(path));
1019         memset(pathtocheck, 0, sizeof(pathtocheck));
1020         strcpy(path, origpath);
1021         last_comp = path;
1022         symlinkcount = 0;
1023         return(NULL);
1024     }
1025
1026     /* We were not given origpath; find then next path to check */
1027     
1028     /* If we've gotten all the way through already, return NULL */
1029     if (last_comp == NULL)
1030         return(NULL);
1031
1032     do {
1033         while (*last_comp == DIR)
1034             strncat(pathtocheck, last_comp++, 1);
1035         len = (elast_comp = strchr(last_comp, DIR)) 
1036             ? elast_comp - last_comp : strlen(last_comp);
1037         strncat(pathtocheck, last_comp, len);
1038         memset(linkbuf, 0, sizeof(linkbuf));
1039         if ((link = (readlink(pathtocheck, linkbuf, 
1040                                     sizeof(linkbuf)) > 0))) {
1041             if (++symlinkcount > MAXSYMLINKS) {
1042                 fprintf(stderr, "%s: %s\n", progname, strerror(ELOOP));
1043                 exit(AKLOG_BADPATH);
1044             }
1045             memset(tmpbuf, 0, sizeof(tmpbuf));
1046             if (elast_comp)
1047                 strcpy(tmpbuf, elast_comp);
1048             if (linkbuf[0] == DIR) {
1049                 /* 
1050                  * If this is a symbolic link to an absolute path, 
1051                  * replace what we have by the absolute path.
1052                  */
1053                 memset(path, 0, strlen(path));
1054                 memcpy(path, linkbuf, sizeof(linkbuf));
1055                 strcat(path, tmpbuf);
1056                 last_comp = path;
1057                 elast_comp = NULL;
1058                 memset(pathtocheck, 0, sizeof(pathtocheck));
1059             }
1060             else {
1061                 /* 
1062                  * If this is a symbolic link to a relative path, 
1063                  * replace only the last component with the link name.
1064                  */
1065                 strncpy(last_comp, linkbuf, strlen(linkbuf) + 1);
1066                 strcat(path, tmpbuf);
1067                 elast_comp = NULL;
1068                 if ((t = strrchr(pathtocheck, DIR))) {
1069                     t++;
1070                     memset(t, 0, strlen(t));
1071                 }
1072                 else
1073                     memset(pathtocheck, 0, sizeof(pathtocheck));
1074             }
1075         }
1076         else
1077             last_comp = elast_comp;
1078     }
1079     while(link);
1080
1081     return(pathtocheck);
1082 }
1083
1084 static void
1085 add_hosts(char *file)
1086 {
1087 #ifdef AFS_SUN_ENV
1088     char V = 'V'; /* AFS has problem on SunOS */
1089 #endif
1090     struct ViceIoctl vio;
1091     char outbuf[BUFSIZ];
1092     long *phosts;
1093     int i;
1094     struct hostent *hp;
1095     struct in_addr in;
1096     
1097     memset(outbuf, 0, sizeof(outbuf));
1098
1099     vio.out_size = sizeof(outbuf);
1100     vio.in_size = 0;
1101     vio.out = outbuf;
1102
1103     if (dflag) {
1104         printf("Getting list of hosts for %s\n", file);
1105     }
1106     /* Don't worry about errors. */
1107     if (!pioctl(file, VIOCWHEREIS, &vio, 1)) {
1108         phosts = (long *) outbuf;
1109
1110         /*
1111          * Lists hosts that we care about.  If ALLHOSTS is defined,
1112          * then all hosts that you ever may possible go through are
1113          * included in this list.  If not, then only hosts that are
1114          * the only ones appear.  That is, if a volume you must use
1115          * is replaced on only one server, that server is included.
1116          * If it is replicated on many servers, then none are included.
1117          * This is not perfect, but the result is that people don't
1118          * get subscribed to a lot of instances of FILSRV that they
1119          * probably won't need which reduces the instances of 
1120          * people getting messages that don't apply to them.
1121          */
1122 #ifndef ALLHOSTS
1123         if (phosts[1] != '\0')
1124             return;
1125 #endif
1126         for (i = 0; phosts[i]; i++) {
1127             if (hosts) {
1128                 in.s_addr = phosts[i];
1129                 if (dflag) {
1130                     printf("Got host %s\n", inet_ntoa(in));
1131                 }
1132                 ll_string(&hostlist, ll_s_add, (char *)inet_ntoa(in));
1133             }
1134             if (zsubs && (hp=gethostbyaddr((char *) &phosts[i],sizeof(long),AF_INET))) {
1135                 if (dflag) {
1136                     printf("Got host %s\n", hp->h_name);
1137                 }
1138                 ll_string(&zsublist, ll_s_add, hp->h_name);
1139             }
1140         }
1141     }
1142 }
1143
1144 /*
1145  * This routine descends through a path to a directory, logging to 
1146  * every cell it encounters along the way.
1147  */
1148 static int
1149 auth_to_path(krb5_context context, char *path)
1150 {
1151     int status = AKLOG_SUCCESS;
1152     int auth_status = AKLOG_SUCCESS;
1153
1154     char *nextpath;
1155     char pathtocheck[MAXPATHLEN + 1];
1156     char mountpoint[MAXPATHLEN + 1];
1157
1158     char *cell;
1159     char *endofcell;
1160
1161     u_char isdirectory;
1162
1163     /* Initialize */
1164     if (path[0] == DIR)
1165         strcpy(pathtocheck, path);
1166     else {
1167         if (getcwd(pathtocheck, sizeof(pathtocheck)) == NULL) {
1168             fprintf(stderr, "Unable to find current working directory:\n");
1169             fprintf(stderr, "%s\n", pathtocheck);
1170             fprintf(stderr, "Try an absolute pathname.\n");
1171             exit(AKLOG_BADPATH);
1172         }
1173         else {
1174             strcat(pathtocheck, DIRSTRING);
1175             strcat(pathtocheck, path);
1176         }
1177     }
1178     next_path(pathtocheck);
1179
1180     /* Go on to the next level down the path */
1181     while ((nextpath = next_path(NULL))) {
1182         strcpy(pathtocheck, nextpath);
1183         if (dflag) {
1184             printf("Checking directory %s\n", pathtocheck);
1185         }
1186         /* 
1187          * If this is an afs mountpoint, determine what cell from 
1188          * the mountpoint name which is of the form 
1189          * #cellname:volumename or %cellname:volumename.
1190          */
1191         if (get_afs_mountpoint(pathtocheck, mountpoint, sizeof(mountpoint))) {
1192             /* skip over the '#' or '%' */
1193             cell = mountpoint + 1;
1194             /* Add this (cell:volumename) to the list of zsubs */
1195             if (zsubs)
1196                 ll_string(&zsublist, ll_s_add, cell);
1197             if (zsubs || hosts)
1198                 add_hosts(pathtocheck);
1199             if ((endofcell = strchr(mountpoint, VOLMARKER))) {
1200                 *endofcell = '\0';
1201                 if ((auth_status = auth_to_cell(context, cell, NULL, NULL))) {
1202                     if (status == AKLOG_SUCCESS)
1203                         status = auth_status;
1204                     else if (status != auth_status)
1205                         status = AKLOG_SOMETHINGSWRONG;
1206                 }
1207             }
1208         }
1209         else {
1210             if (isdir(pathtocheck, &isdirectory) < 0) {
1211                 /*
1212                  * If we've logged and still can't stat, there's
1213                  * a problem... 
1214                  */
1215                 fprintf(stderr, "%s: stat(%s): %s\n", progname, 
1216                         pathtocheck, strerror(errno));
1217                 return(AKLOG_BADPATH);
1218             }
1219             else if (! isdirectory) {
1220                 /* Allow only directories */
1221                 fprintf(stderr, "%s: %s: %s\n", progname, pathtocheck,
1222                         strerror(ENOTDIR));
1223                 return(AKLOG_BADPATH);
1224             }
1225         }
1226     }
1227     
1228
1229     return(status);
1230 }
1231
1232
1233 /* Print usage message and exit */
1234 static void
1235 usage(void)
1236 {
1237     fprintf(stderr, "\nUsage: %s %s%s%s\n", progname,
1238             "[-d] [[-cell | -c] cell [-k krb_realm]] ",
1239             "[[-p | -path] pathname]\n",
1240             "    [-zsubs] [-hosts] [-noauth] [-noprdb] [-force] [-setpag] \n"
1241                 "    [-linked]"
1242 #ifndef HAVE_NO_KRB5_524
1243                 " [-524]"
1244 #endif
1245                 "\n");
1246     fprintf(stderr, "    -d gives debugging information.\n");
1247     fprintf(stderr, "    krb_realm is the kerberos realm of a cell.\n");
1248     fprintf(stderr, "    pathname is the name of a directory to which ");
1249     fprintf(stderr, "you wish to authenticate.\n");
1250     fprintf(stderr, "    -zsubs gives zephyr subscription information.\n");
1251     fprintf(stderr, "    -hosts gives host address information.\n");
1252     fprintf(stderr, "    -noauth does not attempt to get tokens.\n");
1253     fprintf(stderr, "    -noprdb means don't try to determine AFS ID.\n");
1254     fprintf(stderr, "    -force means replace identical tickets. \n");
1255     fprintf(stderr, "    -linked means if AFS node is linked, try both. \n");
1256     fprintf(stderr, "    -setpag set the AFS process authentication group.\n");
1257 #ifndef HAVE_NO_KRB5_524
1258     fprintf(stderr, "    -524 means use the 524 converter instead of V5 directly\n");
1259 #endif
1260     fprintf(stderr, "    No commandline arguments means ");
1261     fprintf(stderr, "authenticate to the local cell.\n");
1262     fprintf(stderr, "\n");
1263     exit(AKLOG_USAGE);
1264 }
1265
1266 int
1267 main(int argc, char *argv[])
1268 {
1269     krb5_context context;
1270     int status = AKLOG_SUCCESS;
1271     int i;
1272     int somethingswrong = FALSE;
1273
1274     cellinfo_t cellinfo;
1275
1276     extern char *progname;      /* Name of this program */
1277
1278     extern int dflag;           /* Debug mode */
1279
1280     int cmode = FALSE;          /* Cellname mode */
1281     int pmode = FALSE;          /* Path name mode */
1282
1283     char realm[REALM_SZ];       /* Kerberos realm of afs server */
1284     char cell[BUFSIZ];          /* Cell to which we are authenticating */
1285     char path[MAXPATHLEN + 1];          /* Path length for path mode */
1286
1287     linked_list cells;          /* List of cells to log to */
1288     linked_list paths;          /* List of paths to log to */
1289     ll_node *cur_node;
1290     char *linkedcell;
1291
1292     memset(&cellinfo, 0, sizeof(cellinfo));
1293
1294     memset(realm, 0, sizeof(realm));
1295     memset(cell, 0, sizeof(cell));
1296     memset(path, 0, sizeof(path));
1297
1298     ll_init(&cells);
1299     ll_init(&paths);
1300
1301     ll_init(&zsublist);
1302     ll_init(&hostlist);
1303
1304     /* Store the program name here for error messages */
1305     if ((progname = strrchr(argv[0], DIR)))
1306         progname++;
1307     else
1308         progname = argv[0];
1309
1310     krb5_init_context(&context);
1311     initialize_ktc_error_table ();
1312     afs_set_com_err_hook(redirect_errors);
1313
1314     /*
1315      * Enable DES enctypes, which are currently still required for AFS.
1316      * krb5_allow_weak_crypto is MIT Kerberos 1.8.  krb5_enctype_enable is
1317      * Heimdal.
1318      */
1319 #if defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO)
1320     krb5_allow_weak_crypto(context, 1);
1321 #elif defined(HAVE_KRB5_ENCTYPE_ENABLE)
1322     i = krb5_enctype_valid(context, ETYPE_DES_CBC_CRC);
1323     if (i)
1324         krb5_enctype_enable(context, ETYPE_DES_CBC_CRC);
1325 #endif
1326
1327     /* Initialize list of cells to which we have authenticated */
1328     ll_init(&authedcells);
1329
1330     /* Parse commandline arguments and make list of what to do. */
1331     for (i = 1; i < argc; i++) {
1332         if (strcmp(argv[i], "-d") == 0)
1333             dflag++;
1334         else if (strcmp(argv[i], "-noauth") == 0) 
1335             noauth++;
1336         else if (strcmp(argv[i], "-zsubs") == 0)
1337             zsubs++;
1338         else if (strcmp(argv[i], "-hosts") == 0)
1339             hosts++;
1340         else if (strcmp(argv[i], "-noprdb") == 0)
1341             noprdb++;
1342         else if (strcmp(argv[i], "-linked") == 0)
1343                 linked++;
1344         else if (strcmp(argv[i], "-force") == 0)
1345             force++;
1346 #ifndef HAVE_NO_KRB5_524
1347         else if (strcmp(argv[i], "-524") == 0)
1348             do524++;
1349 #endif
1350     else if (strcmp(argv[i], "-setpag") == 0)
1351             afssetpag++;
1352         else if (((strcmp(argv[i], "-cell") == 0) ||
1353                   (strcmp(argv[i], "-c") == 0)) && !pmode)
1354             if (++i < argc) {
1355                 cmode++;
1356                 strcpy(cell, argv[i]);
1357             }
1358             else
1359                 usage();
1360         else if ((strcmp(argv[i], "-keytab") == 0))
1361             if (++i < argc) {
1362                 keytab = argv[i];
1363             }
1364             else
1365                 usage();
1366         else if ((strcmp(argv[i], "-principal") == 0))
1367             if (++i < argc) {
1368                 client = argv[i];
1369             }
1370             else
1371                 usage();
1372         else if (((strcmp(argv[i], "-path") == 0) ||
1373                   (strcmp(argv[i], "-p") == 0)) && !cmode)
1374             if (++i < argc) {
1375                 pmode++;
1376                 strcpy(path, argv[i]);
1377             }
1378             else
1379                 usage();
1380             
1381         else if (argv[i][0] == '-')
1382             usage();
1383         else if (!pmode && !cmode) {
1384             if (strchr(argv[i], DIR) || (strcmp(argv[i], ".") == 0) ||
1385                 (strcmp(argv[i], "..") == 0)) {
1386                 pmode++;
1387                 strcpy(path, argv[i]);
1388             }
1389             else { 
1390                 cmode++;
1391                 strcpy(cell, argv[i]);
1392             }
1393         }
1394         else
1395             usage();
1396
1397         if (cmode) {
1398             if (((i + 1) < argc) && (strcmp(argv[i + 1], "-k") == 0)) {
1399                 i+=2;
1400                 if (i < argc)
1401                     strcpy(realm, argv[i]);
1402                 else
1403                     usage();
1404             }
1405             /* Add this cell to list of cells */
1406             strcpy(cellinfo.cell, cell);
1407             strcpy(cellinfo.realm, realm);
1408             if ((cur_node = ll_add_node(&cells, ll_tail))) {
1409                 char *new_cellinfo;
1410                 if ((new_cellinfo = copy_cellinfo(&cellinfo)))
1411                     ll_add_data(cur_node, new_cellinfo);
1412                 else {
1413                     fprintf(stderr, 
1414                             "%s: failure copying cellinfo.\n", progname);
1415                     exit(AKLOG_MISC);
1416                 }
1417             }
1418             else {
1419                 fprintf(stderr, "%s: failure adding cell to cells list.\n",
1420                         progname);
1421                 exit(AKLOG_MISC);
1422             }
1423             memset(&cellinfo, 0, sizeof(cellinfo));
1424             cmode = FALSE;
1425             memset(cell, 0, sizeof(cell));
1426             memset(realm, 0, sizeof(realm));
1427         }
1428         else if (pmode) {
1429             /* Add this path to list of paths */
1430             if ((cur_node = ll_add_node(&paths, ll_tail))) {
1431                 char *new_path;
1432                 if ((new_path = strdup(path)))
1433                     ll_add_data(cur_node, new_path);
1434                 else {
1435                     fprintf(stderr, "%s: failure copying path name.\n",
1436                             progname);
1437                     exit(AKLOG_MISC);
1438                 }
1439             }
1440             else {
1441                 fprintf(stderr, "%s: failure adding path to paths list.\n",
1442                         progname);
1443                 exit(AKLOG_MISC);
1444             }
1445             pmode = FALSE;
1446             memset(path, 0, sizeof(path));
1447         }
1448     }
1449
1450     /* If nothing was given, log to the local cell. */
1451     if ((cells.nelements + paths.nelements) == 0) {
1452         struct passwd *pwd;
1453
1454         status = auth_to_cell(context, NULL, NULL, &linkedcell);
1455         
1456         /* If this cell is linked to a DCE cell, and user requested -linked,
1457          * get tokens for both. This is very useful when the AFS cell is
1458          * linked to a DFS cell and this system does not also have DFS.
1459          */
1460
1461         if (!status && linked && linkedcell != NULL) {
1462             if (dflag) {
1463                 printf("Linked cell: %s\n", linkedcell);
1464             }
1465             status = auth_to_cell(context, linkedcell, NULL, NULL);
1466         }
1467         if (linkedcell) {
1468             free(linkedcell);
1469             linkedcell = NULL;
1470         }
1471
1472         /*
1473          * Local hack - if the person has a file in their home
1474          * directory called ".xlog", read that for a list of
1475          * extra cells to authenticate to
1476          */
1477
1478         if ((pwd = getpwuid(getuid())) != NULL) {
1479             struct stat sbuf;
1480             FILE *f;
1481             char fcell[100], xlog_path[512];
1482
1483             strcpy(xlog_path, pwd->pw_dir);
1484             strcat(xlog_path, "/.xlog");
1485
1486             if ((stat(xlog_path, &sbuf) == 0) &&
1487                 ((f = fopen(xlog_path, "r")) != NULL)) {
1488
1489                 if (dflag) {
1490                     printf("Reading %s for cells to authenticate to.\n",
1491                            xlog_path);
1492                 }
1493
1494                 while (fgets(fcell, 100, f) != NULL) {
1495                     int auth_status;
1496
1497                     fcell[strlen(fcell) - 1] = '\0';
1498
1499                     if (dflag) {
1500                         printf("Found cell %s in %s.\n", fcell, xlog_path);
1501                     }
1502
1503                     auth_status = auth_to_cell(context, fcell, NULL, NULL);
1504                     if (status == AKLOG_SUCCESS)
1505                         status = auth_status;
1506                     else
1507                         status = AKLOG_SOMETHINGSWRONG;
1508                 }
1509             }
1510         }
1511     }
1512     else {
1513         /* Log to all cells in the cells list first */
1514         for (cur_node = cells.first; cur_node; cur_node = cur_node->next) {
1515             memcpy((char *)&cellinfo, cur_node->data, sizeof(cellinfo));
1516             if ((status = auth_to_cell(context, cellinfo.cell, cellinfo.realm,
1517                                        &linkedcell)))
1518                 somethingswrong++;
1519             else {
1520                 if (linked && linkedcell != NULL) {
1521                     if (dflag) {
1522                         printf("Linked cell: %s\n", linkedcell);
1523                     }
1524                     if ((status = auth_to_cell(context, linkedcell,
1525                                                cellinfo.realm, NULL)))
1526                         somethingswrong++;
1527                 }
1528                 if (linkedcell != NULL) {
1529                     free(linkedcell);
1530                     linkedcell = NULL;
1531                 }
1532             }
1533         }
1534
1535         /* Then, log to all paths in the paths list */
1536         for (cur_node = paths.first; cur_node; cur_node = cur_node->next) {
1537             if ((status = auth_to_path(context, cur_node->data)))
1538                 somethingswrong++;
1539         }
1540         
1541         /* 
1542          * If only one thing was logged to, we'll return the status 
1543          * of the single call.  Otherwise, we'll return a generic
1544          * something failed status.
1545          */
1546         if (somethingswrong && ((cells.nelements + paths.nelements) > 1))
1547             status = AKLOG_SOMETHINGSWRONG;
1548     }
1549
1550     /* If we are keeping track of zephyr subscriptions, print them. */
1551     if (zsubs) 
1552         for (cur_node = zsublist.first; cur_node; cur_node = cur_node->next) {
1553             printf("zsub: %s\n", cur_node->data);
1554         }
1555
1556     /* If we are keeping track of host information, print it. */
1557     if (hosts)
1558         for (cur_node = hostlist.first; cur_node; cur_node = cur_node->next) {
1559             printf("host: %s\n", cur_node->data);
1560         }
1561
1562     exit(status);
1563 }
1564
1565 static int
1566 isdir(char *path, unsigned char *val)
1567 {
1568     struct stat statbuf;
1569
1570     if (lstat(path, &statbuf) < 0)
1571         return (-1);
1572     else {
1573         if ((statbuf.st_mode & S_IFMT) == S_IFDIR) 
1574             *val = TRUE;
1575         else
1576             *val = FALSE;
1577         return (0);
1578     }  
1579 }
1580
1581 static krb5_error_code
1582 get_credv5_akimpersonate(krb5_context context,
1583                          char* keytab,
1584                          krb5_principal service_principal,
1585                          krb5_principal client_principal,
1586                          time_t starttime,
1587                          time_t endtime,
1588                          int *allowed_enctypes,
1589                          int *paddress,
1590                          krb5_creds** out_creds /* out */ )
1591 {
1592 #if defined(USING_HEIMDAL) || (defined(HAVE_ENCODE_KRB5_ENC_TKT) && defined(HAVE_ENCODE_KRB5_TICKET) && defined(HAVE_KRB5_C_ENCRYPT))
1593     krb5_error_code code;
1594     krb5_keytab kt = 0;
1595     krb5_kt_cursor cursor[1];
1596     krb5_keytab_entry entry[1];
1597     krb5_ccache cc = 0;
1598     krb5_creds *creds = 0;
1599     krb5_enctype enctype;
1600     krb5_kvno kvno;
1601     krb5_keyblock session_key[1];
1602 #if USING_HEIMDAL
1603     Ticket ticket_reply[1];
1604     EncTicketPart enc_tkt_reply[1];
1605     krb5_address address[30];
1606     krb5_addresses faddr[1];
1607     int temp_vno[1];
1608     time_t temp_time[2];
1609 #else
1610     krb5_ticket ticket_reply[1];
1611     krb5_enc_tkt_part enc_tkt_reply[1];
1612     krb5_address address[30], *faddr[30];
1613 #endif
1614     krb5_data * temp;
1615     int i;
1616     static int any_enctype[] = {0};
1617     *out_creds = 0;
1618     if (!(creds = malloc(sizeof *creds))) {
1619         code = ENOMEM;
1620         goto cleanup;
1621     }
1622     if (!allowed_enctypes)
1623         allowed_enctypes = any_enctype;
1624
1625     cc = 0;
1626     enctype = 0; /* AKIMPERSONATE_IGNORE_ENCTYPE */
1627     kvno = 0; /* AKIMPERSONATE_IGNORE_VNO */
1628     memset((char*)creds, 0, sizeof *creds);
1629     memset((char*)entry, 0, sizeof *entry);
1630     memset((char*)session_key, 0, sizeof *session_key);
1631     memset((char*)ticket_reply, 0, sizeof *ticket_reply);
1632     memset((char*)enc_tkt_reply, 0, sizeof *enc_tkt_reply);
1633     code = krb5_kt_resolve(context, keytab, &kt);
1634     if (code) {
1635         if (keytab)
1636             afs_com_err(progname, code, "while resolving keytab %s", keytab);
1637         else
1638             afs_com_err(progname, code, "while resolving default keytab");
1639         goto cleanup;
1640     }
1641
1642     if (service_principal) {
1643         for (i = 0; (enctype = allowed_enctypes[i]) || !i; ++i) {
1644             code = krb5_kt_get_entry(context,
1645                                      kt,
1646                                      service_principal,
1647                                      kvno,
1648                                      enctype,
1649                                      entry);
1650             if (!code) {
1651                 if (allowed_enctypes[i])
1652                     deref_keyblock_enctype(session_key) = allowed_enctypes[i];
1653                 break;
1654             }
1655         }
1656         if (code) {
1657             afs_com_err(progname, code,"while scanning keytab entries");
1658             goto cleanup;
1659         }
1660     } else {
1661         krb5_keytab_entry new[1];
1662         int best = -1;
1663         memset(new, 0, sizeof *new);
1664         if ((code == krb5_kt_start_seq_get(context, kt, cursor))) {
1665             afs_com_err(progname, code, "while starting keytab scan");
1666             goto cleanup;
1667         }
1668         while (!(code = krb5_kt_next_entry(context, kt, new, cursor))) {
1669             for (i = 0;
1670                     allowed_enctypes[i] && allowed_enctypes[i]
1671                      != deref_entry_enctype(new); ++i)
1672                 ;
1673             if ((!i || allowed_enctypes[i]) &&
1674                 (best < 0 || best > i)) {
1675                 krb5_free_keytab_entry_contents(context, entry);
1676                 *entry = *new;
1677                 memset(new, 0, sizeof *new);
1678             } else krb5_free_keytab_entry_contents(context, new);
1679         }
1680         if ((i = krb5_kt_end_seq_get(context, kt, cursor))) {
1681             afs_com_err(progname, i, "while ending keytab scan");
1682             code = i;
1683             goto cleanup;
1684         }
1685         if (best < 0) {
1686             afs_com_err(progname, code, "while scanning keytab");
1687             goto cleanup;
1688         }
1689         deref_keyblock_enctype(session_key) = deref_entry_enctype(entry);
1690     }
1691
1692     /* Make Ticket */
1693
1694 #if USING_HEIMDAL
1695     if ((code = krb5_generate_random_keyblock(context,
1696                                               deref_keyblock_enctype(session_key), session_key))) {
1697         afs_com_err(progname, code, "while making session key");
1698         goto cleanup;
1699     }
1700     enc_tkt_reply->flags.initial = 1;
1701     enc_tkt_reply->transited.tr_type = DOMAIN_X500_COMPRESS;
1702     enc_tkt_reply->cname = client_principal->name;
1703     enc_tkt_reply->crealm = client_principal->realm;
1704     enc_tkt_reply->key = *session_key;
1705     {
1706         static krb5_data empty_string;
1707         enc_tkt_reply->transited.contents = empty_string;
1708     }
1709     enc_tkt_reply->authtime = starttime;
1710     enc_tkt_reply->starttime = temp_time;
1711     *enc_tkt_reply->starttime = starttime;
1712 #if 0
1713     enc_tkt_reply->renew_till = temp_time + 1;
1714     *enc_tkt_reply->renew_till = endtime;
1715 #endif
1716     enc_tkt_reply->endtime = endtime;
1717 #else
1718     if ((code = krb5_c_make_random_key(context,
1719                                        deref_keyblock_enctype(session_key), session_key))) {
1720         afs_com_err(progname, code, "while making session key");
1721         goto cleanup;
1722     }
1723     enc_tkt_reply->magic = KV5M_ENC_TKT_PART;
1724 #define DATACAST        (unsigned char *)
1725     enc_tkt_reply->flags |= TKT_FLG_INITIAL;
1726     enc_tkt_reply->transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
1727     enc_tkt_reply->session = session_key;
1728     enc_tkt_reply->client = client_principal;
1729     {
1730         static krb5_data empty_string;
1731         enc_tkt_reply->transited.tr_contents = empty_string;
1732     }
1733     enc_tkt_reply->times.authtime = starttime;
1734     enc_tkt_reply->times.starttime = starttime; /* krb524init needs this */
1735     enc_tkt_reply->times.endtime = endtime;
1736 #endif  /* USING_HEIMDAL */
1737     /* NB:  We will discard address for now--ignoring caddr field               
1738        in any case.  MIT branch does what it always did. */
1739
1740     if (paddress && *paddress) {
1741         deref_enc_tkt_addrs(enc_tkt_reply) = faddr;
1742 #if USING_HEIMDAL
1743         faddr->len = 0;
1744         faddr->val = address;
1745 #endif
1746         for (i = 0; paddress[i]; ++i) {
1747 #if USING_HEIMDAL
1748             address[i].addr_type = KRB5_ADDRESS_INET;
1749             address[i].address.data = (void*)(paddress+i);
1750             address[i].address.length = sizeof(paddress[i]);
1751 #else
1752 #if !USING_SSL
1753             address[i].magic = KV5M_ADDRESS;
1754             address[i].addrtype = ADDRTYPE_INET;
1755 #else
1756             address[i].addrtype = AF_INET;
1757 #endif
1758             address[i].contents = (void*)(paddress+i);
1759             address[i].length = sizeof(int);
1760             faddr[i] = address+i;
1761 #endif
1762         }
1763 #if USING_HEIMDAL
1764         faddr->len = i;
1765 #else
1766         faddr[i] = 0;
1767 #endif
1768     }
1769
1770 #if USING_HEIMDAL
1771     ticket_reply->sname = service_principal->name;
1772     ticket_reply->realm = service_principal->realm;
1773
1774     { /* crypto block */
1775         krb5_crypto crypto = 0;
1776         unsigned char *buf = 0;
1777         size_t buf_size, buf_len;
1778         char *what;
1779
1780         ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size,
1781                            enc_tkt_reply, &buf_len, code);
1782         if(code) {
1783             afs_com_err(progname, code, "while encoding ticket");
1784             goto cleanup;
1785         }
1786
1787         if(buf_len != buf_size) {
1788             afs_com_err(progname, code,
1789                     "%d != %d while encoding ticket (internal ASN.1 encoder error",
1790                     buf_len, buf_size);
1791             goto cleanup;
1792         }
1793         what = "krb5_crypto_init";
1794         code = krb5_crypto_init(context,
1795                                 &deref_entry_keyblock(entry),
1796                                 deref_entry_enctype(entry),
1797                                 &crypto);
1798         if(!code) {
1799             what = "krb5_encrypt";
1800             code = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_TICKET,
1801                                               buf, buf_len, entry->vno, &(ticket_reply->enc_part));
1802         }
1803         if (buf) free(buf);
1804         if (crypto) krb5_crypto_destroy(context, crypto);
1805         if(code) {
1806             afs_com_err(progname, code, "while %s", what);
1807             goto cleanup;
1808         }
1809     } /* crypto block */
1810     ticket_reply->enc_part.etype = deref_entry_enctype(entry);
1811     ticket_reply->enc_part.kvno = temp_vno;
1812     *ticket_reply->enc_part.kvno = entry->vno;
1813     ticket_reply->tkt_vno = 5;
1814 #else
1815     ticket_reply->server = service_principal;
1816     ticket_reply->enc_part2 = enc_tkt_reply;
1817     if ((code = krb5_encrypt_tkt_part(context, &deref_entry_keyblock(entry), ticket_reply))) {
1818         afs_com_err(progname, code, "while making ticket");
1819         goto cleanup;
1820     }
1821     ticket_reply->enc_part.kvno = entry->vno;
1822 #endif
1823
1824     /* Construct Creds */
1825
1826     if ((code = krb5_copy_principal(context, service_principal,
1827                                     &creds->server))) {
1828         afs_com_err(progname, code, "while copying service principal");
1829         goto cleanup;
1830     }
1831     if ((code = krb5_copy_principal(context, client_principal,
1832                                     &creds->client))) {
1833         afs_com_err(progname, code, "while copying client principal");
1834         goto cleanup;
1835     }
1836     if ((code = krb5_copy_keyblock_contents(context, session_key,
1837                                             &deref_session_key(creds)))) {
1838         afs_com_err(progname, code, "while copying session key");
1839         goto cleanup;
1840     }
1841
1842 #if USING_HEIMDAL
1843     creds->times.authtime = enc_tkt_reply->authtime;
1844     creds->times.starttime = *(enc_tkt_reply->starttime);
1845     creds->times.endtime = enc_tkt_reply->endtime;
1846     creds->times.renew_till = 0; /* *(enc_tkt_reply->renew_till) */
1847     creds->flags.b = enc_tkt_reply->flags;
1848 #else
1849     creds->times = enc_tkt_reply->times;
1850     creds->ticket_flags = enc_tkt_reply->flags;
1851 #endif
1852     if (!deref_enc_tkt_addrs(enc_tkt_reply))
1853         ;
1854     else if ((code = krb5_copy_addresses(context,
1855                                          deref_enc_tkt_addrs(enc_tkt_reply), &creds->addresses))) {
1856         afs_com_err(progname, code, "while copying addresses");
1857         goto cleanup;
1858     }
1859
1860 #if USING_HEIMDAL
1861     {
1862         size_t creds_tkt_len;
1863         ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
1864                            ticket_reply, &creds_tkt_len, code);
1865         if(code) {
1866             afs_com_err(progname, code, "while encoding ticket");
1867             goto cleanup;
1868         }
1869     }
1870 #else
1871     if ((code = encode_krb5_ticket(ticket_reply, &temp))) {
1872         afs_com_err(progname, code, "while encoding ticket");
1873         goto cleanup;
1874     }
1875     creds->ticket = *temp;
1876     free(temp);
1877 #endif
1878     /* return creds */
1879     *out_creds = creds;
1880     creds = 0;
1881 cleanup:
1882     if (deref_enc_data(&ticket_reply->enc_part))
1883         free(deref_enc_data(&ticket_reply->enc_part));
1884     krb5_free_keytab_entry_contents(context, entry);
1885     if (client_principal)
1886         krb5_free_principal(context, client_principal);
1887     if (service_principal)
1888         krb5_free_principal(context, service_principal);
1889     if (cc)
1890         krb5_cc_close(context, cc);
1891     if (kt)
1892         krb5_kt_close(context, kt);
1893     if (creds) krb5_free_creds(context, creds);
1894     krb5_free_keyblock_contents(context, session_key);
1895 out:
1896     return code;
1897 #else
1898     return -1;
1899 #endif
1900 }
1901
1902
1903 static krb5_error_code
1904 get_credv5(krb5_context context, char *name, char *inst, char *realm,
1905            krb5_creds **creds)
1906 {
1907     krb5_creds increds;
1908     krb5_error_code r;
1909     static krb5_principal client_principal = 0;
1910
1911     if (dflag) {
1912         printf("Getting tickets: %s%s%s@%s\n", name, (inst && inst[0])
1913                ? "/" : "", inst ? inst : "", realm);
1914     }
1915     
1916     memset(&increds, 0, sizeof(increds));
1917 /* ANL - instance may be ptr to a null string. Pass null then */
1918     if ((r = krb5_build_principal(context, &increds.server,
1919                                   strlen(realm), realm,
1920                                   name,
1921                                   (inst && strlen(inst)) ? inst : (void *) NULL,
1922                                   (void *) NULL))) {
1923         return r;
1924     }
1925     
1926
1927     if (!_krb425_ccache) {
1928         r = krb5_cc_default(context, &_krb425_ccache);
1929         if (r)
1930             return r;
1931     }
1932     if (!client_principal) {
1933         if (client) {
1934             r = krb5_parse_name(context, client,  &client_principal);
1935         } else {
1936             r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
1937         }
1938         if (r)
1939             return r;
1940     }
1941     
1942     increds.client = client_principal;
1943     increds.times.endtime = 0;
1944     /* Ask for DES since that is what V4 understands */
1945     get_creds_enctype((&increds)) = ENCTYPE_DES_CBC_CRC;
1946     
1947     if (keytab) {
1948         int allowed_enctypes[] = {
1949             ENCTYPE_DES_CBC_CRC, 0
1950         };
1951
1952         r = get_credv5_akimpersonate(context,
1953                                      keytab,
1954                                      increds.server,
1955                                      increds.client,
1956                                      300, ((~0U)>>1),
1957                                      allowed_enctypes,
1958                                      0 /* paddress */,
1959                                      creds /* out */);
1960     } else {
1961         r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
1962     }    
1963     return r;
1964 }
1965
1966
1967 static int
1968 get_user_realm(krb5_context context, char **realm)
1969 {
1970     static krb5_principal client_principal = 0;
1971     krb5_error_code r = 0;
1972
1973     *realm = NULL;
1974
1975     if (!_krb425_ccache)
1976         krb5_cc_default(context, &_krb425_ccache);
1977     if (!client_principal) {
1978         if (client) {
1979             r = krb5_parse_name(context, client,  &client_principal);
1980         } else {
1981             r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
1982         }
1983         if (r)
1984             return r;
1985     }
1986
1987     *realm = extract_realm(context, client_principal);
1988     if (*realm == NULL)
1989         return ENOMEM;
1990
1991     return(r);
1992 }