2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
14 #include <sys/types.h>
28 #include <afs/com_err.h>
31 #include <afs/afsutil.h>
32 #include <afs/cellconfig.h>
34 #include "rxk5_utilafs.h"
36 #include <afs/ptclient.h>
40 #ifdef HAVE_KRB5_CREDS_KEYBLOCK
43 #ifdef HAVE_KRB5_CREDS_SESSION
44 #define USING_HEIMDAL 1
50 /* This code borrowed heavily from the previous version of log. Here is the
51 intro comment for that program: */
54 log -- tell the Andrew Cache Manager your password
59 Further modified in August 1987 to understand cell IDs.
61 Further modified in October 2006 to understand kerberos 5.
65 klog [principal [password]] [-t] [-c cellname] [-k <k5realm>]
68 principal is of the form 'name' or 'name@cell' which provides the
69 cellname. See the -c option below.
70 password is the user's password. This form is NOT recommended for
72 -t advises klog to write a Kerberos style ticket file in /tmp.
73 -c identifies cellname as the cell in which authentication is to take
75 -k identifies an alternate kerberos realm to use provide
76 authentication services for the cell.
79 #define KLOGEXIT(code) rx_Finalize(); \
81 static int CommandProc(struct cmd_syndesc *as, void *arock);
84 static char **zero_argv;
86 static krb5_context k5context;
87 static struct afsconf_dir *tdir;
88 static int always_evil = 2; /* gcc optimizes 0 into bss. fools. */
91 main(int argc, char *argv[])
93 struct cmd_syndesc *ts;
97 * The following signal action for AIX is necessary so that in case of a
98 * crash (i.e. core is generated) we can include the user's data section
99 * in the core dump. Unfortunately, by default, only a partial core is
100 * generated which, in many cases, isn't too useful.
102 struct sigaction nsa;
104 sigemptyset(&nsa.sa_mask);
105 nsa.sa_handler = SIG_DFL;
106 nsa.sa_flags = SA_FULLDUMP;
107 sigaction(SIGABRT, &nsa, NULL);
108 sigaction(SIGSEGV, &nsa, NULL);
113 ts = cmd_CreateSyntax(NULL, CommandProc, NULL,
114 "obtain Kerberos authentication");
131 cmd_AddParm(ts, "-x", CMD_FLAG, CMD_OPTIONAL|CMD_HIDDEN, 0);
132 cmd_Seek(ts, aPRINCIPAL);
133 cmd_AddParm(ts, "-principal", CMD_SINGLE, CMD_OPTIONAL, "user name");
134 cmd_AddParm(ts, "-password", CMD_SINGLE, CMD_OPTIONAL, "user's password");
135 cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
136 cmd_AddParm(ts, "-k", CMD_SINGLE, CMD_OPTIONAL, "krb5 realm");
137 cmd_AddParm(ts, "-pipe", CMD_FLAG, CMD_OPTIONAL,
138 "read password from stdin");
139 cmd_AddParm(ts, "-silent", CMD_FLAG, CMD_OPTIONAL, "silent operation");
140 cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL,
141 "ticket lifetime in hh[:mm[:ss]]");
142 cmd_AddParm(ts, "-setpag", CMD_FLAG, CMD_OPTIONAL,
143 "Create a new setpag before authenticating");
144 cmd_AddParm(ts, "-tmp", CMD_FLAG, CMD_OPTIONAL,
145 "write Kerberos-style ticket file in /tmp");
146 cmd_AddParm(ts, "-noprdb", CMD_FLAG, CMD_OPTIONAL, "don't consult pt");
147 cmd_AddParm(ts, "-unwrap", CMD_FLAG, CMD_OPTIONAL, "perform 524d conversion");
149 cmd_AddParm(ts, "-k5", CMD_FLAG, CMD_OPTIONAL, "get rxk5 credentials");
150 cmd_AddParm(ts, "-k4", CMD_FLAG, CMD_OPTIONAL, "get rxkad credentials");
152 ++ts->nParms; /* skip -k5 */
153 cmd_AddParm(ts, "-k4", CMD_FLAG, CMD_OPTIONAL|CMD_HIDDEN, 0);
156 code = cmd_Dispatch(argc, argv);
163 static char gpbuf[BUFSIZ];
164 /* read a password from stdin, stop on \n or eof */
166 memset(gpbuf, 0, sizeof(gpbuf));
167 for (i = 0; i < (sizeof(gpbuf) - 1); i++) {
169 if (tc == '\n' || tc == EOF)
177 silent_errors(const char *who,
182 /* ignore and don't print error */
185 #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
187 #define get_princ_str(c, p, n) krb5_princ_component(c, p, n)->data
188 #define get_princ_len(c, p, n) krb5_princ_component(c, p, n)->length
189 #define num_comp(c, p) (krb5_princ_size(c, p))
190 #define realm_data(c, p) krb5_princ_realm(c, p)->data
191 #define realm_len(c, p) krb5_princ_realm(c, p)->length
193 #elif defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
195 #define get_princ_str(c, p, n) krb5_principal_get_comp_string(c, p, n)
196 #define get_princ_len(c, p, n) strlen(krb5_principal_get_comp_string(c, p, n))
197 #define num_comp(c, p) ((p)->name.name_string.len)
198 #define realm_data(c, p) krb5_realm_data(krb5_principal_get_realm(c, p))
199 #define realm_len(c, p) krb5_realm_length(krb5_principal_get_realm(c, p))
202 #error "Must have either krb5_princ_size or krb5_principal_get_comp_string"
205 #if defined(HAVE_KRB5_CREDS_KEYBLOCK)
207 #define get_cred_keydata(c) c->keyblock.contents
208 #define get_cred_keylen(c) c->keyblock.length
209 #define get_creds_enctype(c) c->keyblock.enctype
211 #elif defined(HAVE_KRB5_CREDS_SESSION)
213 #define get_cred_keydata(c) c->session.keyvalue.data
214 #define get_cred_keylen(c) c->session.keyvalue.length
215 #define get_creds_enctype(c) c->session.keytype
218 #error "Must have either keyblock or session member of krb5_creds"
222 whoami(struct ktc_token *atoken,
223 struct afsconf_cell *cellconfig,
224 struct ktc_principal *aclient,
230 struct ubik_client *ptconn = 0;
231 struct rx_securityClass *sc;
232 struct rx_connection *conns[MAXSERVERS+1];
235 char tempname[PR_MAXNAMELEN + 1];
237 memset(lnames, 0, sizeof *lnames);
238 memset(lids, 0, sizeof *lids);
240 sc = rxkad_NewClientSecurityObject(rxkad_auth,
241 &atoken->sessionKey, atoken->kvno,
242 atoken->ticketLen, atoken->ticket);
243 for (i = 0; i < cellconfig->numServers; ++i)
244 conns[i] = rx_NewConnection(cellconfig->hostAddr[i].sin_addr.s_addr,
245 cellconfig->hostAddr[i].sin_port, PRSRV, sc, scIndex);
248 if ((code = ubik_ClientInit(conns, &ptconn)))
250 if (*aclient->instance)
251 snprintf (tempname, sizeof tempname, "%s.%s",
252 aclient->name, aclient->instance);
254 snprintf (tempname, sizeof tempname, "%s", aclient->name);
255 lnames->namelist_len = 1;
256 lnames->namelist_val = (prname *) tempname;
257 code = ubik_PR_NameToID(ptconn, 0, lnames, lids);
258 if (lids->idlist_val) {
259 *vicep = *lids->idlist_val;
262 if (lids->idlist_val) free(lids->idlist_val);
263 if (ptconn) ubik_ClientDestroy(ptconn);
268 k5_to_k4_name(krb5_context k5context,
269 krb5_principal k5princ,
270 struct ktc_principal *ktcprinc)
274 switch(num_comp(k5context, k5princ)) {
277 i = get_princ_len(k5context, k5princ, 1);
278 if (i > MAXKTCNAMELEN-1) i = MAXKTCNAMELEN-1;
279 memcpy(ktcprinc->instance, get_princ_str(k5context, k5princ, 1), i);
282 i = get_princ_len(k5context, k5princ, 0);
283 if (i > MAXKTCNAMELEN-1) i = MAXKTCNAMELEN-1;
284 memcpy(ktcprinc->name, get_princ_str(k5context, k5princ, 0), i);
291 /* save and reuse password. This is necessary to make
292 * "direct to service" authentication work with most
293 * flavors of kerberos, when the afs principal has no instance.
299 klog_prompter(krb5_context context,
304 krb5_prompt prompts[])
306 krb5_error_code code;
308 #ifndef USING_HEIMDAL
309 krb5_prompt_type *types;
311 struct kp_arg *kparg = (struct kp_arg *) a;
312 code = krb5_prompter_posix(context, a, name, banner, num_prompts, prompts);
313 if (code) return code;
314 #ifndef USING_HEIMDAL
315 if ((types = krb5_get_prompt_types(context)))
317 for (i = 0; i < num_prompts; ++i) {
319 type = prompts[i].type;
324 printf ("i%d t%d <%.*s>\n", i,
326 prompts[i].reply->length,
327 prompts[i].reply->data);
330 case KRB5_PROMPT_TYPE_PASSWORD:
331 case KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN:
332 memcpy(kparg->pstore, prompts[i].reply->data, prompts[i].reply->length);
333 kparg->pstore[prompts[i].reply->length] = 0;
334 *kparg->pp = kparg->pstore;
341 CommandProc(struct cmd_syndesc *as, void *arock)
343 krb5_principal princ = 0;
344 char *cell, *pname, **hrealms, *service;
345 char service_temp[MAXKTCREALMLEN + 20];
346 char realm[MAXKTCREALMLEN];
347 char lrealm[MAXKTCREALMLEN]; /* uppercase copy of local cellname */
348 krb5_creds incred[1], mcred[1], *outcred = 0, *afscred;
350 krb5_get_init_creds_opt gic_opts[1];
351 char *tofree, *outname;
354 int i, dosetpag, evil, noprdb, id;
358 krb5_data enc_part[1];
359 time_t lifetime; /* requested ticket lifetime */
360 krb5_prompter_fct pf = NULL;
363 struct kp_arg klog_arg[1];
366 struct afsconf_cell cellconfig[1];
368 static char rn[] = "klog"; /*Routine name */
369 static int Pipe = 0; /* reading from a pipe */
370 static int Silent = 0; /* Don't want error messages */
372 int local; /* explicit cell is same a local one */
373 int writeTicketFile = 0; /* write ticket file to /tmp */
375 char *reason; /* string describing errors */
378 memset(incred, 0, sizeof *incred);
379 /* blow away command line arguments */
380 for (i = 1; i < zero_argc; i++)
381 memset(zero_argv[i], 0, strlen(zero_argv[i]));
383 memset(klog_arg, 0, sizeof *klog_arg);
385 /* first determine quiet flag based on -silent switch */
386 Silent = (as->parms[aSILENT].items ? 1 : 0);
389 afs_set_com_err_hook(silent_errors);
392 if ((code = krb5_init_context(&k5context))) {
393 afs_com_err(rn, code, "while initializing Kerberos 5 library");
396 if ((code = rx_Init(0))) {
397 afs_com_err(rn, code, "while initializing rx");
400 initialize_U_error_table();
401 /*initialize_krb5_error_table();*/
402 initialize_RXK_error_table();
403 initialize_KTC_error_table();
404 initialize_ACFG_error_table();
405 /* initialize_rx_error_table(); */
406 if (!(tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))) {
407 afs_com_err(rn, 0, "can't get afs configuration (afsconf_Open(%s))",
408 rn, AFSDIR_CLIENT_ETC_DIRPATH);
412 /* Parse remaining arguments. */
414 dosetpag = !! as->parms[aSETPAG].items;
415 Pipe = !! as->parms[aPIPE].items;
416 writeTicketFile = !! as->parms[aTMP].items;
417 noprdb = !! as->parms[aNOPRDB].items;
418 evil = (always_evil&1) || !! as->parms[aUNWRAP].items;
422 if (as->parms[aK5].items)
423 authtype |= FORCE_RXK5;
424 if (as->parms[aK4].items)
425 authtype |= FORCE_RXKAD;
427 authtype |= env_afs_rxk5_default();
430 cell = as->parms[aCELL].items ? cell = as->parms[aCELL].items->data : 0;
431 if ((code = afsconf_GetCellInfo(tdir, cell, "afsprot", cellconfig))) {
433 afs_com_err(rn, code, "Can't get cell information for '%s'", cell);
435 afs_com_err(rn, code, "Can't get determine local cell!");
439 if (as->parms[aKRBREALM].items) {
440 code = krb5_set_default_realm(k5context,
441 (const char *) as->parms[aKRBREALM].items);
443 afs_com_err(rn, code, "Can't make <%s> the default realm",
444 as->parms[aKRBREALM].items);
448 else if ((code = krb5_get_host_realm(k5context, cellconfig->hostName[0], &hrealms))) {
449 afs_com_err(rn, code, "Can't get realm for host <%s> in cell <%s>\n",
450 cellconfig->hostName[0], cellconfig->name);
453 if (hrealms && *hrealms) {
454 code = krb5_set_default_realm(k5context,
457 afs_com_err(rn, code, "Can't make <%s> the default realm",
462 if (hrealms) krb5_free_host_realm(k5context, hrealms);
466 if (as->parms[aPRINCIPAL].items) {
467 pname = as->parms[aPRINCIPAL].items->data;
469 /* No explicit name provided: use Unix uid. */
474 "Can't figure out your name from your user id (%d).", id);
476 fprintf(stderr, "%s: Try providing the user name.\n", rn);
481 code = krb5_parse_name(k5context, pname, &princ);
483 afs_com_err(rn, code, "Can't parse principal <%s>", pname);
487 if (as->parms[aPASSWORD].items) {
489 * Current argument is the desired password string. Remember it in
490 * our local buffer, and zero out the argument string - anyone can
491 * see it there with ps!
493 strncpy(passwd, as->parms[aPASSWORD].items->data, sizeof(passwd));
494 memset(as->parms[aPASSWORD].items->data, 0,
495 strlen(as->parms[aPASSWORD].items->data));
499 if (as->parms[aLIFETIME].items) {
500 char *life = as->parms[aLIFETIME].items->data;
501 char *sp; /* string ptr to rest of life */
502 lifetime = 3600 * strtol(life, &sp, 0); /* hours */
506 fprintf(stderr, "%s: translating '%s' to lifetime failed\n",
511 life = sp + 1; /* skip the colon */
512 lifetime += 60 * strtol(life, &sp, 0); /* minutes */
517 lifetime += strtol(life, &sp, 0); /* seconds */
529 /* Get the password if it wasn't provided. */
532 strncpy(passwd, getpipepass(), sizeof(passwd));
542 if (authtype & FORCE_RXK5) {
543 tofree = get_afs_krb5_svc_princ(cellconfig);
544 snprintf(service_temp, sizeof service_temp, "%s", tofree);
547 snprintf (service_temp, sizeof service_temp, "afs/%s", cellconfig->name);
551 service = service_temp;
553 klog_arg->pp = &pass;
554 klog_arg->pstore = passwd;
555 /* XXX should allow k5 to prompt in most cases -- what about expired pw?*/
556 krb5_get_init_creds_opt_init(gic_opts);
558 code = krb5_get_init_creds_password(k5context,
565 service, /* in_tkt_service */
567 if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || service != service_temp) break;
569 if (authtype & FORCE_RXK5) break;
573 memset(passwd, 0, sizeof(passwd));
576 if (krb5_get_default_realm(k5context, &r))
579 afs_com_err(rn, code, "Unable to authenticate to use %s", service);
581 afs_com_err(rn, code, "Unable to authenticate in realm %s", r);
583 afs_com_err(rn, code, "Unable to authenticate to use cell %s",
592 for (;;writeTicketFile = 0) {
593 if (writeTicketFile) {
594 what = "getting default ccache";
595 code = krb5_cc_default(k5context, &cc);
597 what = "krb5_cc_resolve";
598 code = krb5_cc_resolve(k5context, "MEMORY:core", &cc);
599 if (code) goto Failed;
601 what = "initializing ccache";
602 code = krb5_cc_initialize(k5context, cc, princ);
603 if (code) goto Failed;
604 what = "writing Kerberos ticket file";
605 code = krb5_cc_store_cred(k5context, cc, incred);
606 if (code) goto Failed;
609 "Wrote ticket file to %s\n",
610 krb5_cc_get_name(k5context, cc));
614 afs_com_err(rn, code, what);
615 if (writeTicketFile) {
617 krb5_cc_close(k5context, cc);
625 for (service = service_temp;;service = "afs") {
626 memset(mcred, 0, sizeof *mcred);
627 mcred->client = princ;
628 code = krb5_parse_name(k5context, service, &mcred->server);
630 afs_com_err(rn, code, "Unable to parse service <%s>\n", service);
633 if (tofree) { free(tofree); tofree = 0; }
634 if (!(code = krb5_unparse_name(k5context, mcred->server, &outname)))
636 else outname = service;
637 code = krb5_get_credentials(k5context, 0, cc, mcred, &outcred);
638 krb5_free_principal(k5context, mcred->server);
639 if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || service != service_temp) break;
641 if (authtype & FORCE_RXK5) break;
647 afs_com_err(rn, code, "Unable to get credentials to use %s", outname);
652 if (authtype & FORCE_RXK5) {
653 struct ktc_principal aserver[1];
656 memset(aserver, 0, sizeof *aserver);
657 strncpy(aserver->cell, cellconfig->name, MAXKTCREALMLEN-1);
658 code = ktc_SetK5Token(k5context, aserver, afscred, viceid, dosetpag);
660 afs_com_err(rn, code, "Unable to store tokens for cell %s\n",
667 struct ktc_principal aserver[1], aclient[1];
668 struct ktc_token atoken[1];
670 memset(atoken, 0, sizeof *atoken);
672 atoken->kvno = RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY;
673 if (afs_krb5_skip_ticket_wrapper(afscred->ticket.data,
674 afscred->ticket.length, &enc_part->data,
675 &enc_part->length)) {
676 afs_com_err(rn, 0, "Can't unwrap %s AFS credential",
681 atoken->kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
682 *enc_part = afscred->ticket;
684 atoken->startTime = afscred->times.starttime;
685 atoken->endTime = afscred->times.endtime;
686 memcpy(&atoken->sessionKey, get_cred_keydata(afscred),
687 get_cred_keylen(afscred));
688 memcpy(atoken->ticket, enc_part->data,
689 atoken->ticketLen = enc_part->length);
690 memset(aserver, 0, sizeof *aserver);
691 strncpy(aserver->name, "afs", 4);
692 strncpy(aserver->cell, cellconfig->name, MAXKTCREALMLEN-1);
693 memset(aclient, 0, sizeof *aclient);
694 i = realm_len(k5context, afscred->client);
695 if (i > MAXKTCREALMLEN-1) i = MAXKTCREALMLEN-1;
696 memcpy(aclient->cell, realm_data(k5context, afscred->client), i);
699 k5_to_k4_name(k5context, afscred->client, aclient);
700 code = whoami(atoken, cellconfig, aclient, &viceid);
702 afs_com_err(rn, code, "Can't get your viceid", cellconfig->name);
705 snprintf(aclient->name, MAXKTCNAMELEN-1, "AFS ID %d", viceid);
708 k5_to_k4_name(k5context, afscred->client, aclient);
709 code = ktc_SetToken(aserver, atoken, aclient, dosetpag);
711 afs_com_err(rn, code, "Unable to store tokens for cell %s\n",
717 krb5_free_principal(k5context, princ);
718 krb5_free_cred_contents(k5context, incred);
719 if (outcred) krb5_free_creds(k5context, outcred);
721 krb5_cc_close(k5context, cc);
722 if (tofree) free(tofree);