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>
19 #include <afs/com_err.h>
21 #include <afs/afsutil.h>
22 #include <afs/cellconfig.h>
23 #include <afs/ptclient.h>
25 #include <afs/ptuser.h>
27 #define KERBEROS_APPLE_DEPRECATED(x)
30 #ifdef HAVE_KRB5_CREDS_KEYBLOCK
33 #ifdef HAVE_KRB5_CREDS_SESSION
34 #define USING_HEIMDAL 1
39 /* This code borrowed heavily from the previous version of log. Here is the
40 intro comment for that program: */
43 log -- tell the Andrew Cache Manager your password
48 Further modified in August 1987 to understand cell IDs.
50 Further modified in October 2006 to understand kerberos 5.
54 klog [principal [password]] [-t] [-c cellname] [-k <k5realm>]
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
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
64 -k identifies an alternate kerberos realm to use provide
65 authentication services for the cell.
68 #define KLOGEXIT(code) rx_Finalize(); \
70 static int CommandProc(struct cmd_syndesc
*as
, void *arock
);
73 static char **zero_argv
;
75 static krb5_context k5context
;
76 static struct afsconf_dir
*tdir
;
77 static int always_evil
= 2; /* gcc optimizes 0 into bss. fools. */
80 main(int argc
, char *argv
[])
82 struct cmd_syndesc
*ts
;
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.
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
);
102 ts
= cmd_CreateSyntax(NULL
, CommandProc
, NULL
, 0,
103 "obtain Kerberos authentication");
120 cmd_AddParm(ts
, "-x", CMD_FLAG
, CMD_OPTIONAL
, "obsolete, noop");
121 cmd_Seek(ts
, aPRINCIPAL
);
122 cmd_AddParm(ts
, "-principal", CMD_SINGLE
, CMD_OPTIONAL
, "user name");
123 cmd_AddParm(ts
, "-password", CMD_SINGLE
, CMD_OPTIONAL
, "user's password");
124 cmd_AddParm(ts
, "-cell", CMD_SINGLE
, CMD_OPTIONAL
, "cell name");
125 cmd_AddParm(ts
, "-k", CMD_SINGLE
, CMD_OPTIONAL
, "krb5 realm");
126 cmd_AddParm(ts
, "-pipe", CMD_FLAG
, CMD_OPTIONAL
,
127 "read password from stdin");
128 cmd_AddParm(ts
, "-silent", CMD_FLAG
, CMD_OPTIONAL
, "silent operation");
129 cmd_AddParm(ts
, "-lifetime", CMD_SINGLE
, CMD_OPTIONAL
,
130 "ticket lifetime in hh[:mm[:ss]]");
131 cmd_AddParm(ts
, "-setpag", CMD_FLAG
, CMD_OPTIONAL
,
132 "Create a new setpag before authenticating");
133 cmd_AddParm(ts
, "-tmp", CMD_FLAG
, CMD_OPTIONAL
,
134 "write Kerberos-style ticket file in /tmp");
135 cmd_AddParm(ts
, "-noprdb", CMD_FLAG
, CMD_OPTIONAL
, "don't consult pt");
136 cmd_AddParm(ts
, "-unwrap", CMD_FLAG
, CMD_OPTIONAL
, "perform 524d conversion");
138 cmd_AddParm(ts
, "-k5", CMD_FLAG
, CMD_OPTIONAL
, "get rxk5 credentials");
139 cmd_AddParm(ts
, "-k4", CMD_FLAG
, CMD_OPTIONAL
, "get rxkad credentials");
141 ++ts
->nParms
; /* skip -k5 */
142 cmd_AddParm(ts
, "-k4", CMD_FLAG
, CMD_OPTIONAL
|CMD_HIDDEN
, 0);
145 code
= cmd_Dispatch(argc
, argv
);
152 static char gpbuf
[BUFSIZ
];
153 /* read a password from stdin, stop on \n or eof */
155 memset(gpbuf
, 0, sizeof(gpbuf
));
156 for (i
= 0; i
< (sizeof(gpbuf
) - 1); i
++) {
158 if (tc
== '\n' || tc
== EOF
)
166 silent_errors(const char *who
,
171 /* ignore and don't print error */
174 #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
176 #define get_princ_str(c, p, n) krb5_princ_component(c, p, n)->data
177 #define get_princ_len(c, p, n) krb5_princ_component(c, p, n)->length
178 #define num_comp(c, p) (krb5_princ_size(c, p))
179 #define realm_data(c, p) krb5_princ_realm(c, p)->data
180 #define realm_len(c, p) krb5_princ_realm(c, p)->length
182 #elif defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
184 #define get_princ_str(c, p, n) krb5_principal_get_comp_string(c, p, n)
185 #define get_princ_len(c, p, n) strlen(krb5_principal_get_comp_string(c, p, n))
186 #define num_comp(c, p) ((p)->name.name_string.len)
187 #define realm_data(c, p) krb5_realm_data(krb5_principal_get_realm(c, p))
188 #define realm_len(c, p) krb5_realm_length(krb5_principal_get_realm(c, p))
191 #error "Must have either krb5_princ_size or krb5_principal_get_comp_string"
194 #if defined(HAVE_KRB5_CREDS_KEYBLOCK)
196 #define get_cred_keydata(c) c->keyblock.contents
197 #define get_cred_keylen(c) c->keyblock.length
198 #define get_creds_enctype(c) c->keyblock.enctype
200 #elif defined(HAVE_KRB5_CREDS_SESSION)
202 #define get_cred_keydata(c) c->session.keyvalue.data
203 #define get_cred_keylen(c) c->session.keyvalue.length
204 #define get_creds_enctype(c) c->session.keytype
207 #error "Must have either keyblock or session member of krb5_creds"
211 whoami(struct ktc_token
*atoken
,
212 struct afsconf_cell
*cellconfig
,
213 struct ktc_principal
*aclient
,
217 char tempname
[2*PR_MAXNAMELEN
];
219 code
= pr_Initialize(0, AFSDIR_CLIENT_ETC_DIRPATH
, cellconfig
->name
);
223 if (*aclient
->instance
)
224 snprintf (tempname
, sizeof tempname
, "%s.%s",
225 aclient
->name
, aclient
->instance
);
227 snprintf (tempname
, sizeof tempname
, "%s", aclient
->name
);
228 code
= pr_SNameToId(tempname
, vicep
);
234 k5_to_k4_name(krb5_context k5context
,
235 krb5_principal k5princ
,
236 struct ktc_principal
*ktcprinc
)
240 switch(num_comp(k5context
, k5princ
)) {
243 i
= get_princ_len(k5context
, k5princ
, 1);
244 if (i
> MAXKTCNAMELEN
-1) i
= MAXKTCNAMELEN
-1;
245 memcpy(ktcprinc
->instance
, get_princ_str(k5context
, k5princ
, 1), i
);
248 i
= get_princ_len(k5context
, k5princ
, 0);
249 if (i
> MAXKTCNAMELEN
-1) i
= MAXKTCNAMELEN
-1;
250 memcpy(ktcprinc
->name
, get_princ_str(k5context
, k5princ
, 0), i
);
257 #if defined(USING_HEIMDAL) || defined(HAVE_KRB5_PROMPT_TYPE)
259 klog_is_pass_prompt(int index
, krb5_context context
, krb5_prompt prompts
[])
261 switch (prompts
[index
].type
) {
262 case KRB5_PROMPT_TYPE_PASSWORD
:
263 case KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN
:
269 #elif defined(HAVE_KRB5_GET_PROMPT_TYPES)
271 klog_is_pass_prompt(int index
, krb5_context context
, krb5_prompt prompts
[])
273 /* this isn't thread-safe or anything obviously; it just should be good
274 * enough to work with klog */
275 static krb5_prompt_type
*types
= NULL
;
280 types
= krb5_get_prompt_types(context
);
285 switch (types
[index
]) {
286 case KRB5_PROMPT_TYPE_PASSWORD
:
287 case KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN
:
295 klog_is_pass_prompt(int index
, krb5_context context
, krb5_prompt prompts
[])
297 /* AIX 5.3 doesn't have krb5_get_prompt_types. Neither does HP-UX, which
298 * also doesn't even define KRB5_PROMPT_TYPE_PASSWORD &co. We have no way
299 * of determining the the prompt type, so just assume it's a password */
304 /* save and reuse password. This is necessary to make
305 * "direct to service" authentication work with most
306 * flavors of kerberos, when the afs principal has no instance.
313 klog_prompter(krb5_context context
,
318 krb5_prompt prompts
[])
320 krb5_error_code code
;
322 struct kp_arg
*kparg
= (struct kp_arg
*) a
;
325 code
= krb5_prompter_posix(context
, a
, name
, banner
, num_prompts
, prompts
);
326 if (code
) return code
;
327 for (i
= 0; i
< num_prompts
; ++i
) {
328 if (klog_is_pass_prompt(i
, context
, prompts
)) {
329 length
= prompts
[i
].reply
->length
;
330 if (length
> kparg
->allocated
- 1)
331 length
= kparg
->allocated
- 1;
332 memcpy(kparg
->pstore
, prompts
[i
].reply
->data
, length
);
333 kparg
->pstore
[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 krb5_creds incred
[1], mcred
[1], *outcred
= 0, *afscred
;
348 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
349 krb5_get_init_creds_opt
*gic_opts
;
351 krb5_get_init_creds_opt gic_opts
[1];
353 char *tofree
= NULL
, *outname
;
356 int i
, dosetpag
, evil
, noprdb
, id
;
360 krb5_data enc_part
[1];
361 time_t lifetime
; /* requested ticket lifetime */
362 krb5_prompter_fct pf
= NULL
;
365 struct kp_arg klog_arg
[1];
368 struct afsconf_cell cellconfig
[1];
370 static char rn
[] = "klog"; /*Routine name */
371 static int Pipe
= 0; /* reading from a pipe */
372 static int Silent
= 0; /* Don't want error messages */
374 int writeTicketFile
= 0; /* write ticket file to /tmp */
377 memset(incred
, 0, sizeof *incred
);
378 /* blow away command line arguments */
379 for (i
= 1; i
< zero_argc
; i
++)
380 memset(zero_argv
[i
], 0, strlen(zero_argv
[i
]));
382 memset(klog_arg
, 0, sizeof *klog_arg
);
384 /* first determine quiet flag based on -silent switch */
385 Silent
= (as
->parms
[aSILENT
].items
? 1 : 0);
388 afs_set_com_err_hook(silent_errors
);
391 if ((code
= krb5_init_context(&k5context
))) {
392 afs_com_err(rn
, code
, "while initializing Kerberos 5 library");
395 if ((code
= rx_Init(0))) {
396 afs_com_err(rn
, code
, "while initializing rx");
399 initialize_U_error_table();
400 /*initialize_krb5_error_table();*/
401 initialize_RXK_error_table();
402 initialize_KTC_error_table();
403 initialize_ACFG_error_table();
404 /* initialize_rx_error_table(); */
405 if (!(tdir
= afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH
))) {
406 afs_com_err(rn
, 0, "can't get afs configuration (afsconf_Open(%s))",
407 AFSDIR_CLIENT_ETC_DIRPATH
);
412 * Enable DES enctypes, which are currently still required for AFS.
413 * krb5_allow_weak_crypto is MIT Kerberos 1.8. krb5_enctype_enable is
416 #if defined(HAVE_KRB5_ENCTYPE_ENABLE)
417 i
= krb5_enctype_valid(k5context
, ETYPE_DES_CBC_CRC
);
419 krb5_enctype_enable(k5context
, ETYPE_DES_CBC_CRC
);
420 #elif defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO)
421 krb5_allow_weak_crypto(k5context
, 1);
424 /* Parse remaining arguments. */
426 dosetpag
= !! as
->parms
[aSETPAG
].items
;
427 Pipe
= !! as
->parms
[aPIPE
].items
;
428 writeTicketFile
= !! as
->parms
[aTMP
].items
;
429 noprdb
= !! as
->parms
[aNOPRDB
].items
;
430 evil
= (always_evil
&1) || !! as
->parms
[aUNWRAP
].items
;
434 if (as
->parms
[aK5
].items
)
435 authtype
|= FORCE_RXK5
;
436 if (as
->parms
[aK4
].items
)
437 authtype
|= FORCE_RXKAD
;
439 authtype
|= env_afs_rxk5_default();
442 cell
= as
->parms
[aCELL
].items
? as
->parms
[aCELL
].items
->data
: 0;
443 if ((code
= afsconf_GetCellInfo(tdir
, cell
, "afsprot", cellconfig
))) {
445 afs_com_err(rn
, code
, "Can't get cell information for '%s'", cell
);
447 afs_com_err(rn
, code
, "Can't get determine local cell!");
451 if (as
->parms
[aKRBREALM
].items
) {
452 code
= krb5_set_default_realm(k5context
,
453 as
->parms
[aKRBREALM
].items
->data
);
455 afs_com_err(rn
, code
, "Can't make <%s> the default realm",
456 as
->parms
[aKRBREALM
].items
->data
);
460 else if ((code
= krb5_get_host_realm(k5context
, cellconfig
->hostName
[0], &hrealms
))) {
461 afs_com_err(rn
, code
, "Can't get realm for host <%s> in cell <%s>\n",
462 cellconfig
->hostName
[0], cellconfig
->name
);
465 if (hrealms
&& *hrealms
) {
466 code
= krb5_set_default_realm(k5context
,
469 afs_com_err(rn
, code
, "Can't make <%s> the default realm",
474 if (hrealms
) krb5_free_host_realm(k5context
, hrealms
);
478 if (as
->parms
[aPRINCIPAL
].items
) {
479 pname
= as
->parms
[aPRINCIPAL
].items
->data
;
481 /* No explicit name provided: use Unix uid. */
486 "Can't figure out your name from your user id (%d).", id
);
488 fprintf(stderr
, "%s: Try providing the user name.\n", rn
);
493 code
= krb5_parse_name(k5context
, pname
, &princ
);
495 afs_com_err(rn
, code
, "Can't parse principal <%s>", pname
);
499 if (as
->parms
[aPASSWORD
].items
) {
501 * Current argument is the desired password string. Remember it in
502 * our local buffer, and zero out the argument string - anyone can
503 * see it there with ps!
505 strncpy(passwd
, as
->parms
[aPASSWORD
].items
->data
, sizeof(passwd
));
506 memset(as
->parms
[aPASSWORD
].items
->data
, 0,
507 strlen(as
->parms
[aPASSWORD
].items
->data
));
511 if (as
->parms
[aLIFETIME
].items
) {
512 char *life
= as
->parms
[aLIFETIME
].items
->data
;
513 char *sp
; /* string ptr to rest of life */
514 lifetime
= 3600 * strtol(life
, &sp
, 0); /* hours */
518 fprintf(stderr
, "%s: translating '%s' to lifetime failed\n",
523 life
= sp
+ 1; /* skip the colon */
524 lifetime
+= 60 * strtol(life
, &sp
, 0); /* minutes */
529 lifetime
+= strtol(life
, &sp
, 0); /* seconds */
541 /* Get the password if it wasn't provided. */
544 strncpy(passwd
, getpipepass(), sizeof(passwd
));
554 if (authtype
& FORCE_RXK5
) {
555 tofree
= get_afs_krb5_svc_princ(cellconfig
);
556 snprintf(service_temp
, sizeof service_temp
, "%s", tofree
);
559 snprintf (service_temp
, sizeof service_temp
, "afs/%s", cellconfig
->name
);
561 klog_arg
->pp
= &pass
;
562 klog_arg
->pstore
= passwd
;
563 klog_arg
->allocated
= sizeof(passwd
);
564 /* XXX should allow k5 to prompt in most cases -- what about expired pw?*/
565 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
566 code
= krb5_get_init_creds_opt_alloc(k5context
, &gic_opts
);
568 afs_com_err(rn
, code
, "Can't allocate get_init_creds options");
572 krb5_get_init_creds_opt_init(gic_opts
);
576 code
= krb5_get_init_creds_password(k5context
,
583 0, /* in_tkt_service */
585 if (code
!= KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
)
588 memset(passwd
, 0, sizeof(passwd
));
591 if (krb5_get_default_realm(k5context
, &r
))
594 afs_com_err(rn
, code
, "Unable to authenticate in realm %s", r
);
596 afs_com_err(rn
, code
, "Unable to authenticate to use cell %s",
602 for (;;writeTicketFile
= 0) {
603 if (writeTicketFile
) {
604 what
= "getting default ccache";
605 code
= krb5_cc_default(k5context
, &cc
);
607 what
= "krb5_cc_resolve";
608 code
= krb5_cc_resolve(k5context
, "MEMORY:core", &cc
);
609 if (code
) goto Failed
;
611 what
= "initializing ccache";
612 code
= krb5_cc_initialize(k5context
, cc
, princ
);
613 if (code
) goto Failed
;
614 what
= "writing Kerberos ticket file";
615 code
= krb5_cc_store_cred(k5context
, cc
, incred
);
616 if (code
) goto Failed
;
619 "Wrote ticket file to %s\n",
620 krb5_cc_get_name(k5context
, cc
));
624 afs_com_err(rn
, code
, "%s", what
);
625 if (writeTicketFile
) {
627 krb5_cc_close(k5context
, cc
);
635 for (service
= service_temp
;;service
= "afs") {
636 memset(mcred
, 0, sizeof *mcred
);
637 mcred
->client
= princ
;
638 code
= krb5_parse_name(k5context
, service
, &mcred
->server
);
640 afs_com_err(rn
, code
, "Unable to parse service <%s>\n", service
);
643 if (tofree
) { free(tofree
); tofree
= 0; }
644 if (!(code
= krb5_unparse_name(k5context
, mcred
->server
, &outname
)))
646 else outname
= service
;
647 code
= krb5_get_credentials(k5context
, 0, cc
, mcred
, &outcred
);
648 krb5_free_principal(k5context
, mcred
->server
);
649 if (code
!= KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
|| service
!= service_temp
) break;
651 if (authtype
& FORCE_RXK5
)
658 afs_com_err(rn
, code
, "Unable to get credentials to use %s", outname
);
663 if (authtype
& FORCE_RXK5
) {
664 struct ktc_principal aserver
[1];
667 memset(aserver
, 0, sizeof *aserver
);
668 strncpy(aserver
->cell
, cellconfig
->name
, MAXKTCREALMLEN
-1);
669 code
= ktc_SetK5Token(k5context
, aserver
, afscred
, viceid
, dosetpag
);
671 afs_com_err(rn
, code
, "Unable to store tokens for cell %s\n",
678 struct ktc_principal aserver
[1], aclient
[1];
679 struct ktc_token atoken
[1];
681 memset(atoken
, 0, sizeof *atoken
);
683 size_t elen
= enc_part
->length
;
684 atoken
->kvno
= RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY
;
685 if (afs_krb5_skip_ticket_wrapper(afscred
->ticket
.data
,
686 afscred
->ticket
.length
, (char **) &enc_part
->data
,
688 afs_com_err(rn
, 0, "Can't unwrap %s AFS credential",
693 atoken
->kvno
= RXKAD_TKT_TYPE_KERBEROS_V5
;
694 *enc_part
= afscred
->ticket
;
696 atoken
->startTime
= afscred
->times
.starttime
;
697 atoken
->endTime
= afscred
->times
.endtime
;
698 if (tkt_DeriveDesKey(get_creds_enctype(afscred
),
699 get_cred_keydata(afscred
),
700 get_cred_keylen(afscred
), &atoken
->sessionKey
)) {
702 "Cannot derive DES key from enctype %i of length %u",
703 get_creds_enctype(afscred
),
704 (unsigned)get_cred_keylen(afscred
));
707 memcpy(atoken
->ticket
, enc_part
->data
,
708 atoken
->ticketLen
= enc_part
->length
);
709 memset(aserver
, 0, sizeof *aserver
);
710 strncpy(aserver
->name
, "afs", 4);
711 strncpy(aserver
->cell
, cellconfig
->name
, MAXKTCREALMLEN
-1);
712 memset(aclient
, 0, sizeof *aclient
);
713 i
= realm_len(k5context
, afscred
->client
);
714 if (i
> MAXKTCREALMLEN
-1) i
= MAXKTCREALMLEN
-1;
715 memcpy(aclient
->cell
, realm_data(k5context
, afscred
->client
), i
);
718 k5_to_k4_name(k5context
, afscred
->client
, aclient
);
719 code
= whoami(atoken
, cellconfig
, aclient
, &viceid
);
721 afs_com_err(rn
, code
, "Can't get your viceid for cell %s", cellconfig
->name
);
724 snprintf(aclient
->name
, MAXKTCNAMELEN
-1, "AFS ID %d", viceid
);
727 k5_to_k4_name(k5context
, afscred
->client
, aclient
);
728 code
= ktc_SetToken(aserver
, atoken
, aclient
, dosetpag
);
730 afs_com_err(rn
, code
, "Unable to store tokens for cell %s\n",
736 krb5_free_principal(k5context
, princ
);
737 krb5_free_cred_contents(k5context
, incred
);
738 if (outcred
) krb5_free_creds(k5context
, outcred
);
740 krb5_cc_close(k5context
, cc
);
741 if (tofree
) free(tofree
);