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