1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
4 #include "init_creds_ctx.h"
7 krb5_get_as_key_password(krb5_context context,
10 krb5_prompter_fct prompter,
14 krb5_keyblock *as_key,
23 krb5_prompt_type prompt_type;
25 password = (krb5_data *) gak_data;
27 /* If there's already a key of the correct etype, we're done.
28 If the etype is wrong, free the existing key, and make
31 XXX This was the old behavior, and was wrong in hw preauth
32 cases. Is this new behavior -- always asking -- correct in all
36 if (as_key->enctype != etype) {
37 krb5_free_keyblock_contents (context, as_key);
42 if (password->length == 0 || password->data[0] == '\0') {
46 if ((ret = krb5_unparse_name(context, client, &clientstr)))
49 snprintf(promptstr, sizeof(promptstr), _("Password for %s"),
53 prompt.prompt = promptstr;
55 prompt.reply = password;
56 prompt_type = KRB5_PROMPT_TYPE_PASSWORD;
58 /* PROMPTER_INVOCATION */
59 krb5int_set_prompt_types(context, &prompt_type);
60 ret = (*prompter)(context, prompter_data, NULL, NULL, 1, &prompt);
61 krb5int_set_prompt_types(context, 0);
66 if (salt->length == SALT_TYPE_AFS_LENGTH && salt->data == NULL) {
67 if ((ret = krb5_principal2salt(context, client, &defsalt)))
75 ret = krb5_c_string_to_key_with_params(context, etype, password, salt,
76 params->data?params:NULL, as_key);
84 krb5_error_code KRB5_CALLCONV
85 krb5_init_creds_set_password(krb5_context context,
86 krb5_init_creds_context ctx,
95 if (ctx->password.data != NULL) {
96 zap(ctx->password.data, ctx->password.length);
97 krb5_free_data_contents(context, &ctx->password);
100 ctx->password.data = s;
101 ctx->password.length = strlen(s);
102 ctx->gak_fct = krb5_get_as_key_password;
103 ctx->gak_data = &ctx->password;
108 /* Return the password expiry time indicated by enc_part2. Set *is_last_req
109 * if the information came from a last_req value. */
111 get_expiry_times(krb5_enc_kdc_rep_part *enc_part2, krb5_timestamp *pw_exp,
112 krb5_timestamp *acct_exp, krb5_boolean *is_last_req)
114 krb5_last_req_entry **last_req;
119 *is_last_req = FALSE;
121 /* Look for last-req entries for password or account expiration. */
122 if (enc_part2->last_req) {
123 for (last_req = enc_part2->last_req; *last_req; last_req++) {
124 lr_type = (*last_req)->lr_type;
125 if (lr_type == KRB5_LRQ_ALL_PW_EXPTIME ||
126 lr_type == KRB5_LRQ_ONE_PW_EXPTIME) {
128 *pw_exp = (*last_req)->value;
129 } else if (lr_type == KRB5_LRQ_ALL_ACCT_EXPTIME ||
130 lr_type == KRB5_LRQ_ONE_ACCT_EXPTIME) {
132 *acct_exp = (*last_req)->value;
137 /* If we didn't find any, use the ambiguous key_exp field. */
138 if (*is_last_req == FALSE)
139 *pw_exp = enc_part2->key_exp;
143 * Send an appropriate warning prompter if as_reply indicates that the password
144 * is going to expire soon. If an expire callback was provided, use that
148 warn_pw_expiry(krb5_context context, krb5_get_init_creds_opt *options,
149 krb5_prompter_fct prompter, void *data,
150 const char *in_tkt_service, krb5_kdc_rep *as_reply)
153 krb5_timestamp pw_exp, acct_exp, now;
154 krb5_boolean is_last_req;
156 krb5_gic_opt_ext *opte;
157 char ts[256], banner[1024];
159 get_expiry_times(as_reply->enc_part2, &pw_exp, &acct_exp, &is_last_req);
161 ret = krb5int_gic_opt_to_opte(context, options, &opte, 0, "");
162 if (ret == 0 && opte->opt_private->expire_cb != NULL) {
163 krb5_expire_callback_func cb = opte->opt_private->expire_cb;
164 void *cb_data = opte->opt_private->expire_data;
166 /* Invoke the expire callback and don't send prompter warnings. */
167 (*cb)(context, cb_data, pw_exp, acct_exp, is_last_req);
171 /* Don't warn if no password expiry value was sent. */
175 /* Don't warn if the password is being changed. */
176 if (in_tkt_service && strcmp(in_tkt_service, "kadmin/changepw") == 0)
180 * If the expiry time came from a last_req field, assume the KDC wants us
181 * to warn. Otherwise, warn only if the expiry time is less than a week
184 ret = krb5_timeofday(context, &now);
188 (pw_exp < now || (pw_exp - now) > 7 * 24 * 60 * 60))
194 ret = krb5_timestamp_to_string(pw_exp, ts, sizeof(ts));
198 delta = pw_exp - now;
200 snprintf(banner, sizeof(banner),
201 _("Warning: Your password will expire in less than one hour "
203 } else if (delta < 86400*2) {
204 snprintf(banner, sizeof(banner),
205 _("Warning: Your password will expire in %d hour%s on %s"),
206 delta / 3600, delta < 7200 ? "" : "s", ts);
208 snprintf(banner, sizeof(banner),
209 _("Warning: Your password will expire in %d days on %s"),
213 /* PROMPTER_INVOCATION */
214 (*prompter)(context, data, 0, banner, 0, 0);
217 krb5_error_code KRB5_CALLCONV
218 krb5_get_init_creds_password(krb5_context context,
220 krb5_principal client,
222 krb5_prompter_fct prompter,
224 krb5_deltat start_time,
225 char *in_tkt_service,
226 krb5_get_init_creds_opt *options)
228 krb5_error_code ret, ret2;
230 krb5_kdc_rep *as_reply;
232 krb5_creds chpw_creds;
233 krb5_get_init_creds_opt *chpw_opts = NULL;
235 char banner[1024], pw0array[1024], pw1array[1024];
236 krb5_prompt prompt[2];
237 krb5_prompt_type prompt_types[sizeof(prompt)/sizeof(prompt[0])];
241 memset(&chpw_creds, 0, sizeof(chpw_creds));
245 if (password && password[0]) {
246 if (strlcpy(pw0.data, password, sizeof(pw0array)) >= sizeof(pw0array)) {
250 pw0.length = strlen(password);
253 pw0.length = sizeof(pw0array);
258 pw1.length = sizeof(pw1array);
260 /* first try: get the requested tkt from any kdc */
262 ret = krb5int_get_init_creds(context, creds, client, prompter, data,
263 start_time, in_tkt_service, options,
264 krb5_get_as_key_password, (void *) &pw0,
265 &use_master, &as_reply);
267 /* check for success */
272 /* If all the kdc's are unavailable, or if the error was due to a
273 user interrupt, fail */
275 if ((ret == KRB5_KDC_UNREACH) ||
276 (ret == KRB5_LIBOS_PWDINTR) ||
277 (ret == KRB5_REALM_CANT_RESOLVE))
280 /* if the reply did not come from the master kdc, try again with
284 TRACE_GIC_PWD_MASTER(context);
288 krb5_free_kdc_rep( context, as_reply);
291 ret2 = krb5int_get_init_creds(context, creds, client, prompter, data,
292 start_time, in_tkt_service, options,
293 krb5_get_as_key_password, (void *) &pw0,
294 &use_master, &as_reply);
301 /* if the master is unreachable, return the error from the
302 slave we were able to contact or reset the use_master flag */
304 if ((ret2 != KRB5_KDC_UNREACH) &&
305 (ret2 != KRB5_REALM_CANT_RESOLVE) &&
306 (ret2 != KRB5_REALM_UNKNOWN))
313 if (ret == KRB5KDC_ERR_KEY_EXP)
314 goto cleanup; /* Login library will deal appropriately with this error */
317 /* at this point, we have an error from the master. if the error
318 is not password expired, or if it is but there's no prompter,
321 if ((ret != KRB5KDC_ERR_KEY_EXP) ||
325 /* historically the default has been to prompt for password change.
326 * if the change password prompt option has not been set, we continue
327 * to prompt. Prompting is only disabled if the option has been set
328 * and the value has been set to false.
330 if (options && !(options->flags & KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT))
332 TRACE_GIC_PWD_EXPIRED(context);
334 /* ok, we have an expired password. Give the user a few chances
337 /* use a minimal set of options */
339 ret = krb5_get_init_creds_opt_alloc(context, &chpw_opts);
342 krb5_get_init_creds_opt_set_tkt_life(chpw_opts, 5*60);
343 krb5_get_init_creds_opt_set_renew_life(chpw_opts, 0);
344 krb5_get_init_creds_opt_set_forwardable(chpw_opts, 0);
345 krb5_get_init_creds_opt_set_proxiable(chpw_opts, 0);
347 if ((ret = krb5int_get_init_creds(context, &chpw_creds, client,
349 start_time, "kadmin/changepw", chpw_opts,
350 krb5_get_as_key_password, (void *) &pw0,
354 prompt[0].prompt = _("Enter new password");
355 prompt[0].hidden = 1;
356 prompt[0].reply = &pw0;
357 prompt_types[0] = KRB5_PROMPT_TYPE_NEW_PASSWORD;
359 prompt[1].prompt = _("Enter it again");
360 prompt[1].hidden = 1;
361 prompt[1].reply = &pw1;
362 prompt_types[1] = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
364 strlcpy(banner, _("Password expired. You must change it now."),
367 for (tries = 3; tries; tries--) {
368 TRACE_GIC_PWD_CHANGEPW(context, tries);
369 pw0.length = sizeof(pw0array);
370 pw1.length = sizeof(pw1array);
372 /* PROMPTER_INVOCATION */
373 krb5int_set_prompt_types(context, prompt_types);
374 ret = (*prompter)(context, data, 0, banner,
375 sizeof(prompt)/sizeof(prompt[0]), prompt);
376 krb5int_set_prompt_types(context, 0);
380 if (strcmp(pw0.data, pw1.data) != 0) {
381 ret = KRB5_LIBOS_BADPWDMATCH;
382 snprintf(banner, sizeof(banner),
383 _("%s. Please try again."), error_message(ret));
384 } else if (pw0.length == 0) {
385 ret = KRB5_CHPW_PWDNULL;
386 snprintf(banner, sizeof(banner),
387 _("%s. Please try again."), error_message(ret));
390 krb5_data code_string;
391 krb5_data result_string;
393 if ((ret = krb5_change_password(context, &chpw_creds, pw0array,
394 &result_code, &code_string,
398 /* the change succeeded. go on */
400 if (result_code == 0) {
401 free(result_string.data);
405 /* set this in case the retry loop falls through */
407 ret = KRB5_CHPW_FAIL;
409 if (result_code != KRB5_KPASSWD_SOFTERROR) {
410 free(result_string.data);
414 /* the error was soft, so try again */
416 /* 100 is I happen to know that no code_string will be longer
419 if (result_string.length > (sizeof(banner)-100))
420 result_string.length = sizeof(banner)-100;
422 snprintf(banner, sizeof(banner),
423 _("%.*s%s%.*s. Please try again.\n"),
424 (int) code_string.length, code_string.data,
425 result_string.length ? ": " : "",
426 (int) result_string.length,
427 result_string.data ? result_string.data : "");
429 free(code_string.data);
430 free(result_string.data);
437 /* the password change was successful. Get an initial ticket
438 from the master. this is the last try. the return from this
441 TRACE_GIC_PWD_CHANGED(context);
442 ret = krb5int_get_init_creds(context, creds, client, prompter, data,
443 start_time, in_tkt_service, options,
444 krb5_get_as_key_password, (void *) &pw0,
445 &use_master, &as_reply);
451 warn_pw_expiry(context, options, prompter, data, in_tkt_service,
455 krb5_get_init_creds_opt_free(context, chpw_opts);
456 memset(pw0array, 0, sizeof(pw0array));
457 memset(pw1array, 0, sizeof(pw1array));
458 krb5_free_cred_contents(context, &chpw_creds);
460 krb5_free_kdc_rep(context, as_reply);
466 Rewrites get_in_tkt in terms of newer get_init_creds API.
467 Attempts to get an initial ticket for creds->client to use server
468 creds->server, (realm is taken from creds->client), with options
469 options, and using creds->times.starttime, creds->times.endtime,
470 creds->times.renew_till as from, till, and rtime.
471 creds->times.renew_till is ignored unless the RENEWABLE option is requested.
473 If addrs is non-NULL, it is used for the addresses requested. If it is
474 null, the system standard addresses are used.
476 If password is non-NULL, it is converted using the cryptosystem entry
477 point for a string conversion routine, seeded with the client's name.
478 If password is passed as NULL, the password is read from the terminal,
479 and then converted into a key.
481 A succesful call will place the ticket in the credentials cache ccache.
483 returns system errors, encryption errors
485 krb5_error_code KRB5_CALLCONV
486 krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options,
487 krb5_address *const *addrs, krb5_enctype *ktypes,
488 krb5_preauthtype *pre_auth_types,
489 const char *password, krb5_ccache ccache,
490 krb5_creds *creds, krb5_kdc_rep **ret_as_reply)
492 krb5_error_code retval;
496 krb5_principal server_princ, client_princ;
498 krb5_get_init_creds_opt *opts = NULL;
501 if (password && password[0]) {
502 if (strlcpy(pw0.data, password, sizeof(pw0array)) >= sizeof(pw0array))
504 pw0.length = strlen(password);
507 pw0.length = sizeof(pw0array);
509 retval = krb5int_populate_gic_opt(context, &opts,
510 options, addrs, ktypes,
511 pre_auth_types, creds);
514 retval = krb5_unparse_name( context, creds->server, &server);
516 krb5_get_init_creds_opt_free(context, opts);
519 server_princ = creds->server;
520 client_princ = creds->client;
521 retval = krb5int_get_init_creds(context, creds, creds->client,
522 krb5_prompter_posix, NULL,
524 krb5_get_as_key_password, &pw0,
525 &use_master, ret_as_reply);
526 krb5_free_unparsed_name( context, server);
527 krb5_get_init_creds_opt_free(context, opts);
531 krb5_free_principal( context, creds->server);
532 krb5_free_principal( context, creds->client);
533 creds->client = client_princ;
534 creds->server = server_princ;
535 /* store it in the ccache! */
537 if ((retval = krb5_cc_store_cred(context, ccache, creds)))