fd65ee146755425ad5ae788d0eabc7207d30d9f4
[openafs.git] / src / aklog / klog.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <rx/xdr.h>
17 #include <lock.h>
18 #include <ubik.h>
19 #include <afs/com_err.h>
20 #include <afs/auth.h>
21 #include <afs/afsutil.h>
22 #include <afs/cellconfig.h>
23 #include <afs/ptclient.h>
24 #include <afs/cmd.h>
25 #include <afs/ptuser.h>
26
27 #include <krb5.h>
28
29 #ifdef HAVE_KRB5_CREDS_KEYBLOCK
30 #define USING_MIT 1
31 #endif
32 #ifdef HAVE_KRB5_CREDS_SESSION
33 #define USING_HEIMDAL 1
34 #endif
35
36 #include "skipwrap.h"
37
38 /* This code borrowed heavily from the previous version of log.  Here is the
39    intro comment for that program: */
40
41 /*
42         log -- tell the Andrew Cache Manager your password
43         5 June 1985
44         modified
45         February 1986
46
47         Further modified in August 1987 to understand cell IDs.
48
49         Further modified in October 2006 to understand kerberos 5.
50  */
51
52 /* Current Usage:
53      klog [principal [password]] [-t] [-c cellname] [-k <k5realm>]
54
55      where:
56        principal is of the form 'name' or 'name@cell' which provides the
57           cellname.  See the -c option below.
58        password is the user's password.  This form is NOT recommended for
59           interactive users.
60        -t advises klog to write a Kerberos style ticket file in /tmp.
61        -c identifies cellname as the cell in which authentication is to take
62           place.
63        -k identifies an alternate kerberos realm to use provide
64           authentication services for the cell.
65  */
66
67 #define KLOGEXIT(code) rx_Finalize(); \
68                        (exit(!!code))
69 static int CommandProc(struct cmd_syndesc *as, void *arock);
70
71 static int zero_argc;
72 static char **zero_argv;
73
74 static krb5_context k5context;
75 static struct afsconf_dir *tdir;
76 static int always_evil = 2;     /* gcc optimizes 0 into bss.  fools. */
77
78 int
79 main(int argc, char *argv[])
80 {
81     struct cmd_syndesc *ts;
82     afs_int32 code;
83 #ifdef  AFS_AIX32_ENV
84     /*
85      * The following signal action for AIX is necessary so that in case of a
86      * crash (i.e. core is generated) we can include the user's data section
87      * in the core dump. Unfortunately, by default, only a partial core is
88      * generated which, in many cases, isn't too useful.
89      */
90     struct sigaction nsa;
91
92     sigemptyset(&nsa.sa_mask);
93     nsa.sa_handler = SIG_DFL;
94     nsa.sa_flags = SA_FULLDUMP;
95     sigaction(SIGABRT, &nsa, NULL);
96     sigaction(SIGSEGV, &nsa, NULL);
97 #endif
98     zero_argc = argc;
99     zero_argv = argv;
100
101     ts = cmd_CreateSyntax(NULL, CommandProc, NULL,
102                           "obtain Kerberos authentication");
103
104 #define aXFLAG 0
105 #define aPRINCIPAL 1
106 #define aPASSWORD 2
107 #define aCELL 3
108 #define aKRBREALM 4
109 #define aPIPE 5
110 #define aSILENT 6
111 #define aLIFETIME 7
112 #define aSETPAG 8
113 #define aTMP 9
114 #define aNOPRDB 10
115 #define aUNWRAP 11
116 #define aK5 12
117 #define aK4 13
118
119     cmd_AddParm(ts, "-x", CMD_FLAG, CMD_OPTIONAL, "obsolete, noop");
120     cmd_Seek(ts, aPRINCIPAL);
121     cmd_AddParm(ts, "-principal", CMD_SINGLE, CMD_OPTIONAL, "user name");
122     cmd_AddParm(ts, "-password", CMD_SINGLE, CMD_OPTIONAL, "user's password");
123     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
124     cmd_AddParm(ts, "-k", CMD_SINGLE, CMD_OPTIONAL, "krb5 realm");
125     cmd_AddParm(ts, "-pipe", CMD_FLAG, CMD_OPTIONAL,
126                 "read password from stdin");
127     cmd_AddParm(ts, "-silent", CMD_FLAG, CMD_OPTIONAL, "silent operation");
128     cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL,
129                 "ticket lifetime in hh[:mm[:ss]]");
130     cmd_AddParm(ts, "-setpag", CMD_FLAG, CMD_OPTIONAL,
131                 "Create a new setpag before authenticating");
132     cmd_AddParm(ts, "-tmp", CMD_FLAG, CMD_OPTIONAL,
133                 "write Kerberos-style ticket file in /tmp");
134     cmd_AddParm(ts, "-noprdb", CMD_FLAG, CMD_OPTIONAL, "don't consult pt");
135     cmd_AddParm(ts, "-unwrap", CMD_FLAG, CMD_OPTIONAL, "perform 524d conversion");
136 #ifdef AFS_RXK5
137     cmd_AddParm(ts, "-k5", CMD_FLAG, CMD_OPTIONAL, "get rxk5 credentials");
138     cmd_AddParm(ts, "-k4", CMD_FLAG, CMD_OPTIONAL, "get rxkad credentials");
139 #else
140     ++ts->nParms;       /* skip -k5 */
141     cmd_AddParm(ts, "-k4", CMD_FLAG, CMD_OPTIONAL|CMD_HIDDEN, 0);
142 #endif
143
144     code = cmd_Dispatch(argc, argv);
145     KLOGEXIT(code);
146 }
147
148 static char *
149 getpipepass(void)
150 {
151     static char gpbuf[BUFSIZ];
152     /* read a password from stdin, stop on \n or eof */
153     int i, tc;
154     memset(gpbuf, 0, sizeof(gpbuf));
155     for (i = 0; i < (sizeof(gpbuf) - 1); i++) {
156         tc = fgetc(stdin);
157         if (tc == '\n' || tc == EOF)
158             break;
159         gpbuf[i] = tc;
160     }
161     return gpbuf;
162 }
163
164 void
165 silent_errors(const char *who,
166     afs_int32 code,
167     const char *fmt,
168     va_list ap)
169 {
170     /* ignore and don't print error */
171 }
172
173 #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
174
175 #define get_princ_str(c, p, n) krb5_princ_component(c, p, n)->data
176 #define get_princ_len(c, p, n) krb5_princ_component(c, p, n)->length
177 #define num_comp(c, p) (krb5_princ_size(c, p))
178 #define realm_data(c, p) krb5_princ_realm(c, p)->data
179 #define realm_len(c, p) krb5_princ_realm(c, p)->length
180
181 #elif defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
182
183 #define get_princ_str(c, p, n) krb5_principal_get_comp_string(c, p, n)
184 #define get_princ_len(c, p, n) strlen(krb5_principal_get_comp_string(c, p, n))
185 #define num_comp(c, p) ((p)->name.name_string.len)
186 #define realm_data(c, p) krb5_realm_data(krb5_principal_get_realm(c, p))
187 #define realm_len(c, p) krb5_realm_length(krb5_principal_get_realm(c, p))
188
189 #else
190 #error "Must have either krb5_princ_size or krb5_principal_get_comp_string"
191 #endif
192
193 #if defined(HAVE_KRB5_CREDS_KEYBLOCK)
194
195 #define get_cred_keydata(c) c->keyblock.contents
196 #define get_cred_keylen(c) c->keyblock.length
197 #define get_creds_enctype(c) c->keyblock.enctype
198
199 #elif defined(HAVE_KRB5_CREDS_SESSION)
200
201 #define get_cred_keydata(c) c->session.keyvalue.data
202 #define get_cred_keylen(c) c->session.keyvalue.length
203 #define get_creds_enctype(c) c->session.keytype
204
205 #else
206 #error "Must have either keyblock or session member of krb5_creds"
207 #endif
208
209 static int
210 whoami(struct ktc_token *atoken,
211     struct afsconf_cell *cellconfig,
212     struct ktc_principal *aclient,
213     int *vicep)
214 {
215     int code;
216     char tempname[PR_MAXNAMELEN + 1];
217
218     code = pr_Initialize(0, AFSDIR_CLIENT_ETC_DIRPATH, cellconfig->name);
219     if (code)
220         goto Failed;
221
222     if (*aclient->instance)
223         snprintf (tempname, sizeof tempname, "%s.%s",
224             aclient->name, aclient->instance);
225     else
226         snprintf (tempname, sizeof tempname, "%s", aclient->name);
227     code = pr_SNameToId(tempname, vicep);
228 Failed:
229     return code;
230 }
231
232 static void
233 k5_to_k4_name(krb5_context k5context,
234     krb5_principal k5princ,
235     struct ktc_principal *ktcprinc)
236 {
237     int i;
238
239     switch(num_comp(k5context, k5princ)) {
240         default:
241         /* case 2: */
242             i = get_princ_len(k5context, k5princ, 1);
243             if (i > MAXKTCNAMELEN-1) i = MAXKTCNAMELEN-1;
244             memcpy(ktcprinc->instance, get_princ_str(k5context, k5princ, 1), i);
245             /* fall through */
246         case 1:
247             i = get_princ_len(k5context, k5princ, 0);
248             if (i > MAXKTCNAMELEN-1) i = MAXKTCNAMELEN-1;
249             memcpy(ktcprinc->name, get_princ_str(k5context, k5princ, 0), i);
250             /* fall through */
251         case 0:
252             break;
253         }
254 }
255
256 #if defined(USING_HEIMDAL) || defined(HAVE_KRB5_PROMPT_TYPE)
257 static int
258 klog_is_pass_prompt(int index, krb5_context context, krb5_prompt prompts[])
259 {
260     switch (prompts[index].type) {
261     case KRB5_PROMPT_TYPE_PASSWORD:
262     case KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN:
263         return 1;
264     default:
265         return 0;
266     }
267 }
268 #elif defined(HAVE_KRB5_GET_PROMPT_TYPES)
269 static int
270 klog_is_pass_prompt(int index, krb5_context context, krb5_prompt prompts[])
271 {
272     /* this isn't thread-safe or anything obviously; it just should be good
273      * enough to work with klog */
274     static krb5_prompt_type *types = NULL;
275     if (index == 0) {
276         types = NULL;
277     }
278     if (!types) {
279         types = krb5_get_prompt_types(context);
280     }
281     if (!types) {
282         return 0;
283     }
284     switch (types[index]) {
285     case KRB5_PROMPT_TYPE_PASSWORD:
286     case KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN:
287         return 1;
288     default:
289         return 0;
290     }
291 }
292 #else
293 static int
294 klog_is_pass_prompt(int index, krb5_context context, krb5_prompt prompts[])
295 {
296     /* AIX 5.3 doesn't have krb5_get_prompt_types. Neither does HP-UX, which
297      * also doesn't even define KRB5_PROMPT_TYPE_PASSWORD &co. We have no way
298      * of determining the the prompt type, so just assume it's a password */
299     return 1;
300 }
301 #endif
302
303 /* save and reuse password.  This is necessary to make
304  *  "direct to service" authentication work with most
305  *  flavors of kerberos, when the afs principal has no instance.
306  */
307 struct kp_arg {
308     char **pp, *pstore;
309     size_t allocated;
310 };
311 krb5_error_code
312 klog_prompter(krb5_context context,
313     void *a,
314     const char *name,
315     const char *banner,
316     int num_prompts,
317     krb5_prompt prompts[])
318 {
319     krb5_error_code code;
320     int i;
321     struct kp_arg *kparg = (struct kp_arg *) a;
322     size_t length;
323
324     code = krb5_prompter_posix(context, a, name, banner, num_prompts, prompts);
325     if (code) return code;
326     for (i = 0; i < num_prompts; ++i) {
327         if (klog_is_pass_prompt(i, context, prompts)) {
328             length = prompts[i].reply->length;
329             if (length > kparg->allocated - 1)
330                 length = kparg->allocated - 1;
331             memcpy(kparg->pstore, prompts[i].reply->data, length);
332             kparg->pstore[length] = 0;
333             *kparg->pp = kparg->pstore;
334         }
335     }
336     return 0;
337 }
338
339 static int
340 CommandProc(struct cmd_syndesc *as, void *arock)
341 {
342     krb5_principal princ = 0;
343     char *cell, *pname, **hrealms, *service;
344     char service_temp[MAXKTCREALMLEN + 20];
345     krb5_creds incred[1], mcred[1], *outcred = 0, *afscred;
346     krb5_ccache cc = 0;
347 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
348     krb5_get_init_creds_opt *gic_opts;
349 #else
350     krb5_get_init_creds_opt gic_opts[1];
351 #endif
352     char *tofree = NULL, *outname;
353     int code;
354     char *what;
355     int i, dosetpag, evil, noprdb, id;
356 #ifdef AFS_RXK5
357     int authtype;
358 #endif
359     krb5_data enc_part[1];
360     time_t lifetime;            /* requested ticket lifetime */
361     krb5_prompter_fct pf = NULL;
362     char *pass = 0;
363     void *pa = 0;
364     struct kp_arg klog_arg[1];
365
366     char passwd[BUFSIZ];
367     struct afsconf_cell cellconfig[1];
368
369     static char rn[] = "klog";  /*Routine name */
370     static int Pipe = 0;        /* reading from a pipe */
371     static int Silent = 0;      /* Don't want error messages */
372
373     int writeTicketFile = 0;    /* write ticket file to /tmp */
374
375     service = 0;
376     memset(incred, 0, sizeof *incred);
377     /* blow away command line arguments */
378     for (i = 1; i < zero_argc; i++)
379         memset(zero_argv[i], 0, strlen(zero_argv[i]));
380     zero_argc = 0;
381     memset(klog_arg, 0, sizeof *klog_arg);
382
383     /* first determine quiet flag based on -silent switch */
384     Silent = (as->parms[aSILENT].items ? 1 : 0);
385
386     if (Silent) {
387         afs_set_com_err_hook(silent_errors);
388     }
389
390     if ((code = krb5_init_context(&k5context))) {
391         afs_com_err(rn, code, "while initializing Kerberos 5 library");
392         KLOGEXIT(code);
393     }
394     if ((code = rx_Init(0))) {
395         afs_com_err(rn, code, "while initializing rx");
396         KLOGEXIT(code);
397     }
398     initialize_U_error_table();
399     /*initialize_krb5_error_table();*/
400     initialize_RXK_error_table();
401     initialize_KTC_error_table();
402     initialize_ACFG_error_table();
403     /* initialize_rx_error_table(); */
404     if (!(tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))) {
405         afs_com_err(rn, 0, "can't get afs configuration (afsconf_Open(%s))",
406             AFSDIR_CLIENT_ETC_DIRPATH);
407         KLOGEXIT(1);
408     }
409
410     /*
411      * Enable DES enctypes, which are currently still required for AFS.
412      * krb5_allow_weak_crypto is MIT Kerberos 1.8.  krb5_enctype_enable is
413      * Heimdal.
414      */
415 #if defined(HAVE_KRB5_ENCTYPE_ENABLE)
416     i = krb5_enctype_valid(k5context, ETYPE_DES_CBC_CRC);
417     if (i)
418         krb5_enctype_enable(k5context, ETYPE_DES_CBC_CRC);
419 #elif defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO)
420     krb5_allow_weak_crypto(k5context, 1);
421 #endif
422
423     /* Parse remaining arguments. */
424
425     dosetpag = !! as->parms[aSETPAG].items;
426     Pipe = !! as->parms[aPIPE].items;
427     writeTicketFile = !! as->parms[aTMP].items;
428     noprdb = !! as->parms[aNOPRDB].items;
429     evil = (always_evil&1) || !! as->parms[aUNWRAP].items;
430
431 #ifdef AFS_RXK5
432     authtype = 0;
433     if (as->parms[aK5].items)
434         authtype |= FORCE_RXK5;
435     if (as->parms[aK4].items)
436         authtype |= FORCE_RXKAD;
437     if (!authtype)
438         authtype |= env_afs_rxk5_default();
439 #endif
440
441     cell = as->parms[aCELL].items ? as->parms[aCELL].items->data : 0;
442     if ((code = afsconf_GetCellInfo(tdir, cell, "afsprot", cellconfig))) {
443         if (cell)
444             afs_com_err(rn, code, "Can't get cell information for '%s'", cell);
445         else
446             afs_com_err(rn, code, "Can't get determine local cell!");
447         KLOGEXIT(code);
448     }
449
450     if (as->parms[aKRBREALM].items) {
451         code = krb5_set_default_realm(k5context,
452                 as->parms[aKRBREALM].items->data);
453         if (code) {
454             afs_com_err(rn, code, "Can't make <%s> the default realm",
455                 as->parms[aKRBREALM].items->data);
456             KLOGEXIT(code);
457         }
458     }
459     else if ((code = krb5_get_host_realm(k5context, cellconfig->hostName[0], &hrealms))) {
460         afs_com_err(rn, code, "Can't get realm for host <%s> in cell <%s>\n",
461                 cellconfig->hostName[0], cellconfig->name);
462         KLOGEXIT(code);
463     } else {
464         if (hrealms && *hrealms) {
465             code = krb5_set_default_realm(k5context,
466                     *hrealms);
467             if (code) {
468                 afs_com_err(rn, code, "Can't make <%s> the default realm",
469                     *hrealms);
470                 KLOGEXIT(code);
471             }
472         }
473         if (hrealms) krb5_free_host_realm(k5context, hrealms);
474     }
475
476     id = getuid();
477     if (as->parms[aPRINCIPAL].items) {
478         pname = as->parms[aPRINCIPAL].items->data;
479     } else {
480         /* No explicit name provided: use Unix uid. */
481         struct passwd *pw;
482         pw = getpwuid(id);
483         if (pw == 0) {
484             afs_com_err(rn, 0,
485                 "Can't figure out your name from your user id (%d).", id);
486             if (!Silent)
487                 fprintf(stderr, "%s: Try providing the user name.\n", rn);
488             KLOGEXIT(1);
489         }
490         pname = pw->pw_name;
491     }
492     code = krb5_parse_name(k5context, pname, &princ);
493     if (code) {
494         afs_com_err(rn, code, "Can't parse principal <%s>", pname);
495         KLOGEXIT(code);
496     }
497
498     if (as->parms[aPASSWORD].items) {
499         /*
500          * Current argument is the desired password string.  Remember it in
501          * our local buffer, and zero out the argument string - anyone can
502          * see it there with ps!
503          */
504         strncpy(passwd, as->parms[aPASSWORD].items->data, sizeof(passwd));
505         memset(as->parms[aPASSWORD].items->data, 0,
506                strlen(as->parms[aPASSWORD].items->data));
507         pass = passwd;
508     }
509
510     if (as->parms[aLIFETIME].items) {
511         char *life = as->parms[aLIFETIME].items->data;
512         char *sp;               /* string ptr to rest of life */
513         lifetime = 3600 * strtol(life, &sp, 0); /* hours */
514         if (sp == life) {
515           bad_lifetime:
516             if (!Silent)
517                 fprintf(stderr, "%s: translating '%s' to lifetime failed\n",
518                         rn, life);
519             return 1;
520         }
521         if (*sp == ':') {
522             life = sp + 1;      /* skip the colon */
523             lifetime += 60 * strtol(life, &sp, 0);      /* minutes */
524             if (sp == life)
525                 goto bad_lifetime;
526             if (*sp == ':') {
527                 life = sp + 1;
528                 lifetime += strtol(life, &sp, 0);       /* seconds */
529                 if (sp == life)
530                     goto bad_lifetime;
531                 if (*sp)
532                     goto bad_lifetime;
533             } else if (*sp)
534                 goto bad_lifetime;
535         } else if (*sp)
536             goto bad_lifetime;
537     } else
538         lifetime = 0;
539
540     /* Get the password if it wasn't provided. */
541     if (!pass) {
542         if (Pipe) {
543             strncpy(passwd, getpipepass(), sizeof(passwd));
544             pass = passwd;
545         } else {
546             pf = klog_prompter;
547             pa = klog_arg;
548         }
549     }
550
551     service = 0;
552 #ifdef AFS_RXK5
553     if (authtype & FORCE_RXK5) {
554         tofree = get_afs_krb5_svc_princ(cellconfig);
555         snprintf(service_temp, sizeof service_temp, "%s", tofree);
556     } else
557 #endif
558     snprintf (service_temp, sizeof service_temp, "afs/%s", cellconfig->name);
559
560     klog_arg->pp = &pass;
561     klog_arg->pstore = passwd;
562     klog_arg->allocated = sizeof(passwd);
563     /* XXX should allow k5 to prompt in most cases -- what about expired pw?*/
564 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
565     code = krb5_get_init_creds_opt_alloc(k5context, &gic_opts);
566     if (code) {
567         afs_com_err(rn, code, "Can't allocate get_init_creds options");
568         KLOGEXIT(code);
569     }
570 #else
571     krb5_get_init_creds_opt_init(gic_opts);
572 #endif
573
574     for (;;) {
575         code = krb5_get_init_creds_password(k5context,
576             incred,
577             princ,
578             pass,
579             pf, /* prompter */
580             pa, /* data */
581             0,  /* start_time */
582             0,  /* in_tkt_service */
583             gic_opts);
584         if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
585             break;
586     }
587     memset(passwd, 0, sizeof(passwd));
588     if (code) {
589         char *r = 0;
590         if (krb5_get_default_realm(k5context, &r))
591             r = 0;
592         if (r)
593             afs_com_err(rn, code, "Unable to authenticate in realm %s", r);
594         else
595             afs_com_err(rn, code, "Unable to authenticate to use cell %s",
596                 cellconfig->name);
597         if (r) free(r);
598         KLOGEXIT(code);
599     }
600
601     for (;;writeTicketFile = 0) {
602         if (writeTicketFile) {
603             what = "getting default ccache";
604             code = krb5_cc_default(k5context, &cc);
605         } else {
606             what = "krb5_cc_resolve";
607             code = krb5_cc_resolve(k5context, "MEMORY:core", &cc);
608             if (code) goto Failed;
609         }
610         what = "initializing ccache";
611         code = krb5_cc_initialize(k5context, cc, princ);
612         if (code) goto Failed;
613         what = "writing Kerberos ticket file";
614         code = krb5_cc_store_cred(k5context, cc, incred);
615         if (code) goto Failed;
616         if (writeTicketFile)
617             fprintf(stderr,
618                     "Wrote ticket file to %s\n",
619                     krb5_cc_get_name(k5context, cc));
620         break;
621       Failed:
622         if (code)
623             afs_com_err(rn, code, "%s", what);
624         if (writeTicketFile) {
625             if (cc) {
626                 krb5_cc_close(k5context, cc);
627                 cc = 0;
628             }
629             continue;
630         }
631         KLOGEXIT(code);
632     }
633
634     for (service = service_temp;;service = "afs") {
635         memset(mcred, 0, sizeof *mcred);
636         mcred->client = princ;
637         code = krb5_parse_name(k5context, service, &mcred->server);
638         if (code) {
639             afs_com_err(rn, code, "Unable to parse service <%s>\n", service);
640             KLOGEXIT(code);
641         }
642         if (tofree) { free(tofree); tofree = 0; }
643         if (!(code = krb5_unparse_name(k5context, mcred->server, &outname)))
644             tofree = outname;
645         else outname = service;
646         code = krb5_get_credentials(k5context, 0, cc, mcred, &outcred);
647         krb5_free_principal(k5context, mcred->server);
648         if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || service != service_temp) break;
649 #ifdef AFS_RXK5
650         if (authtype & FORCE_RXK5)
651             break;
652 #endif
653     }
654     afscred = outcred;
655
656     if (code) {
657         afs_com_err(rn, code, "Unable to get credentials to use %s", outname);
658         KLOGEXIT(code);
659     }
660
661 #ifdef AFS_RXK5
662     if (authtype & FORCE_RXK5) {
663         struct ktc_principal aserver[1];
664         int viceid = 555;
665
666         memset(aserver, 0, sizeof *aserver);
667         strncpy(aserver->cell, cellconfig->name, MAXKTCREALMLEN-1);
668         code = ktc_SetK5Token(k5context, aserver, afscred, viceid, dosetpag);
669         if (code) {
670             afs_com_err(rn, code, "Unable to store tokens for cell %s\n",
671                 cellconfig->name);
672             KLOGEXIT(1);
673         }
674     } else
675 #endif
676     {
677         struct ktc_principal aserver[1], aclient[1];
678         struct ktc_token atoken[1];
679
680         memset(atoken, 0, sizeof *atoken);
681         if (evil) {
682             size_t elen = enc_part->length;
683             atoken->kvno = RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY;
684             if (afs_krb5_skip_ticket_wrapper(afscred->ticket.data,
685                         afscred->ticket.length, (char **) &enc_part->data,
686                         &elen)) {
687                 afs_com_err(rn, 0, "Can't unwrap %s AFS credential",
688                     cellconfig->name);
689                 KLOGEXIT(1);
690             }
691         } else {
692             atoken->kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
693             *enc_part = afscred->ticket;
694         }
695         atoken->startTime = afscred->times.starttime;
696         atoken->endTime = afscred->times.endtime;
697         if (tkt_DeriveDesKey(get_creds_enctype(afscred),
698                              get_cred_keydata(afscred),
699                              get_cred_keylen(afscred), &atoken->sessionKey)) {
700             afs_com_err(rn, 0,
701                         "Cannot derive DES key from enctype %i of length %u",
702                         get_creds_enctype(afscred),
703                         (unsigned)get_cred_keylen(afscred));
704             KLOGEXIT(1);
705         }
706         memcpy(atoken->ticket, enc_part->data,
707             atoken->ticketLen = enc_part->length);
708         memset(aserver, 0, sizeof *aserver);
709         strncpy(aserver->name, "afs", 4);
710         strncpy(aserver->cell, cellconfig->name, MAXKTCREALMLEN-1);
711         memset(aclient, 0, sizeof *aclient);
712         i = realm_len(k5context, afscred->client);
713         if (i > MAXKTCREALMLEN-1) i = MAXKTCREALMLEN-1;
714         memcpy(aclient->cell, realm_data(k5context, afscred->client), i);
715         if (!noprdb) {
716             int viceid = 0;
717             k5_to_k4_name(k5context, afscred->client, aclient);
718             code = whoami(atoken, cellconfig, aclient, &viceid);
719             if (code) {
720                 afs_com_err(rn, code, "Can't get your viceid for cell %s", cellconfig->name);
721                 *aclient->name = 0;
722             } else
723                 snprintf(aclient->name, MAXKTCNAMELEN-1, "AFS ID %d", viceid);
724         }
725         if (!*aclient->name)
726             k5_to_k4_name(k5context, afscred->client, aclient);
727         code = ktc_SetToken(aserver, atoken, aclient, dosetpag);
728         if (code) {
729             afs_com_err(rn, code, "Unable to store tokens for cell %s\n",
730                 cellconfig->name);
731             KLOGEXIT(1);
732         }
733     }
734
735     krb5_free_principal(k5context, princ);
736     krb5_free_cred_contents(k5context, incred);
737     if (outcred) krb5_free_creds(k5context, outcred);
738     if (cc)
739         krb5_cc_close(k5context, cc);
740     if (tofree) free(tofree);
741
742     return 0;
743 }