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