krb5_error_code KRB5_CALLCONV krb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options, krb5_address *const *addrs, krb5_enctype *ktypes, krb5_preauthtype *pre_auth_types, krb5_keytab arg_keytab, krb5_ccache ccache, krb5_creds *creds, krb5_kdc_rep **ret_as_reply) { krb5_error_code retval; krb5_get_init_creds_opt *opts; char * server = NULL; krb5_keytab keytab; krb5_principal client_princ, server_princ; int use_master = 0; retval = krb5int_populate_gic_opt(context, &opts, options, addrs, ktypes, pre_auth_types, creds); if (retval) return retval; if (arg_keytab == NULL) { retval = krb5_kt_default(context, &keytab); if (retval) goto cleanup; } else keytab = arg_keytab; retval = krb5_unparse_name( context, creds->server, &server); if (retval) goto cleanup; server_princ = creds->server; client_princ = creds->client; retval = krb5int_get_init_creds(context, creds, creds->client, krb5_prompter_posix, NULL, 0, server, opts, get_as_key_keytab, (void *)keytab, &use_master, ret_as_reply); krb5_free_unparsed_name( context, server); if (retval) { goto cleanup; } krb5_free_principal(context, creds->server); krb5_free_principal(context, creds->client); creds->client = client_princ; creds->server = server_princ; /* store it in the ccache! */ if (ccache) if ((retval = krb5_cc_store_cred(context, ccache, creds))) goto cleanup; cleanup: krb5_get_init_creds_opt_free(context, opts); if (arg_keytab == NULL) krb5_kt_close(context, keytab); return retval; }
/* Rewrites get_in_tkt in terms of newer get_init_creds API. Attempts to get an initial ticket for creds->client to use server creds->server, (realm is taken from creds->client), with options options, and using creds->times.starttime, creds->times.endtime, creds->times.renew_till as from, till, and rtime. creds->times.renew_till is ignored unless the RENEWABLE option is requested. If addrs is non-NULL, it is used for the addresses requested. If it is null, the system standard addresses are used. If password is non-NULL, it is converted using the cryptosystem entry point for a string conversion routine, seeded with the client's name. If password is passed as NULL, the password is read from the terminal, and then converted into a key. A succesful call will place the ticket in the credentials cache ccache. returns system errors, encryption errors */ krb5_error_code KRB5_CALLCONV krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options, krb5_address *const *addrs, krb5_enctype *ktypes, krb5_preauthtype *pre_auth_types, const char *password, krb5_ccache ccache, krb5_creds *creds, krb5_kdc_rep **ret_as_reply) { krb5_error_code retval; krb5_data pw0; char pw0array[1024]; char * server; krb5_principal server_princ, client_princ; int use_master = 0; krb5_get_init_creds_opt *opts = NULL; pw0.data = pw0array; if (password && password[0]) { if (strlcpy(pw0.data, password, sizeof(pw0array)) >= sizeof(pw0array)) return EINVAL; pw0.length = strlen(password); } else { pw0.data[0] = '\0'; pw0.length = sizeof(pw0array); } retval = krb5int_populate_gic_opt(context, &opts, options, addrs, ktypes, pre_auth_types, creds); if (retval) return (retval); retval = krb5_unparse_name( context, creds->server, &server); if (retval) { krb5_get_init_creds_opt_free(context, opts); return (retval); } server_princ = creds->server; client_princ = creds->client; retval = krb5int_get_init_creds(context, creds, creds->client, krb5_prompter_posix, NULL, 0, server, opts, krb5_get_as_key_password, &pw0, &use_master, ret_as_reply); krb5_free_unparsed_name( context, server); krb5_get_init_creds_opt_free(context, opts); if (retval) { return (retval); } krb5_free_principal( context, creds->server); krb5_free_principal( context, creds->client); creds->client = client_princ; creds->server = server_princ; /* store it in the ccache! */ if (ccache) if ((retval = krb5_cc_store_cred(context, ccache, creds))) return (retval); return retval; }
/* Similar to krb5_get_in_tkt_with_password. Attempts to get an initial ticket for creds->client to use server creds->server, (realm is taken from creds->client), with options options, and using creds->times.starttime, creds->times.endtime, creds->times.renew_till as from, till, and rtime. creds->times.renew_till is ignored unless the RENEWABLE option is requested. If addrs is non-NULL, it is used for the addresses requested. If it is null, the system standard addresses are used. If keyblock is NULL, an appropriate key for creds->client is retrieved from the system key store (e.g. /etc/srvtab). If keyblock is non-NULL, it is used as the decryption key. A succesful call will place the ticket in the credentials cache ccache. returns system errors, encryption errors */ krb5_error_code KRB5_CALLCONV krb5_get_in_tkt_with_skey(krb5_context context, krb5_flags options, krb5_address *const *addrs, krb5_enctype *ktypes, krb5_preauthtype *pre_auth_types, const krb5_keyblock *key, krb5_ccache ccache, krb5_creds *creds, krb5_kdc_rep **ret_as_reply) { krb5_error_code retval; char *server; krb5_principal server_princ, client_princ; int use_master = 0; krb5_get_init_creds_opt *opts = NULL; #ifndef LEAN_CLIENT if (key == NULL) { return krb5_get_in_tkt_with_keytab(context, options, addrs, ktypes, pre_auth_types, NULL, ccache, creds, ret_as_reply); } #endif /* LEAN_CLIENT */ retval = krb5int_populate_gic_opt(context, &opts, options, addrs, ktypes, pre_auth_types, creds); if (retval) return retval; retval = krb5_unparse_name(context, creds->server, &server); if (retval) { krb5_get_init_creds_opt_free(context, opts); return retval; } server_princ = creds->server; client_princ = creds->client; retval = krb5int_get_init_creds(context, creds, creds->client, krb5_prompter_posix, NULL, 0, server, opts, get_as_key_skey, (void *) key, &use_master, ret_as_reply); krb5_free_unparsed_name(context, server); krb5_get_init_creds_opt_free(context, opts); if (retval) return retval; krb5_free_principal( context, creds->server); krb5_free_principal( context, creds->client); creds->client = client_princ; creds->server = server_princ; /* store it in the ccache! */ if (ccache) retval = krb5_cc_store_cred(context, ccache, creds); return retval; }
krb5_error_code KRB5_CALLCONV krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_principal client, const char *password, krb5_prompter_fct prompter, void *data, krb5_deltat start_time, const char *in_tkt_service, krb5_get_init_creds_opt *options) { krb5_error_code ret, ret2; int use_master; krb5_kdc_rep *as_reply; int tries; krb5_creds chpw_creds; krb5_get_init_creds_opt *chpw_opts = NULL; krb5_data pw0, pw1; char banner[1024], pw0array[1024], pw1array[1024]; krb5_prompt prompt[2]; krb5_prompt_type prompt_types[sizeof(prompt)/sizeof(prompt[0])]; char *message; use_master = 0; as_reply = NULL; memset(&chpw_creds, 0, sizeof(chpw_creds)); pw0.data = pw0array; if (password && password[0]) { if (strlcpy(pw0.data, password, sizeof(pw0array)) >= sizeof(pw0array)) { ret = EINVAL; goto cleanup; } pw0.length = strlen(password); } else { pw0.data[0] = '\0'; pw0.length = sizeof(pw0array); } pw1.data = pw1array; pw1.data[0] = '\0'; pw1.length = sizeof(pw1array); /* first try: get the requested tkt from any kdc */ ret = krb5int_get_init_creds(context, creds, client, prompter, data, start_time, in_tkt_service, options, krb5_get_as_key_password, (void *) &pw0, &use_master, &as_reply); /* check for success */ if (ret == 0) goto cleanup; /* If all the kdc's are unavailable, or if the error was due to a user interrupt, fail */ if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_LIBOS_PWDINTR) || (ret == KRB5_REALM_CANT_RESOLVE)) goto cleanup; /* if the reply did not come from the master kdc, try again with the master kdc */ if (!use_master) { TRACE_GIC_PWD_MASTER(context); use_master = 1; if (as_reply) { krb5_free_kdc_rep( context, as_reply); as_reply = NULL; } ret2 = krb5int_get_init_creds(context, creds, client, prompter, data, start_time, in_tkt_service, options, krb5_get_as_key_password, (void *) &pw0, &use_master, &as_reply); if (ret2 == 0) { ret = 0; goto cleanup; } /* if the master is unreachable, return the error from the slave we were able to contact or reset the use_master flag */ if ((ret2 != KRB5_KDC_UNREACH) && (ret2 != KRB5_REALM_CANT_RESOLVE) && (ret2 != KRB5_REALM_UNKNOWN)) ret = ret2; else use_master = 0; } /* at this point, we have an error from the master. if the error is not password expired, or if it is but there's no prompter, return this error */ if ((ret != KRB5KDC_ERR_KEY_EXP) || (prompter == NULL)) goto cleanup; /* historically the default has been to prompt for password change. * if the change password prompt option has not been set, we continue * to prompt. Prompting is only disabled if the option has been set * and the value has been set to false. */ if (options && !(options->flags & KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT)) goto cleanup; TRACE_GIC_PWD_EXPIRED(context); /* ok, we have an expired password. Give the user a few chances to change it */ /* use a minimal set of options */ ret = krb5_get_init_creds_opt_alloc(context, &chpw_opts); if (ret) goto cleanup; krb5_get_init_creds_opt_set_tkt_life(chpw_opts, 5*60); krb5_get_init_creds_opt_set_renew_life(chpw_opts, 0); krb5_get_init_creds_opt_set_forwardable(chpw_opts, 0); krb5_get_init_creds_opt_set_proxiable(chpw_opts, 0); if ((ret = krb5int_get_init_creds(context, &chpw_creds, client, prompter, data, start_time, "kadmin/changepw", chpw_opts, krb5_get_as_key_password, (void *) &pw0, &use_master, NULL))) goto cleanup; prompt[0].prompt = _("Enter new password"); prompt[0].hidden = 1; prompt[0].reply = &pw0; prompt_types[0] = KRB5_PROMPT_TYPE_NEW_PASSWORD; prompt[1].prompt = _("Enter it again"); prompt[1].hidden = 1; prompt[1].reply = &pw1; prompt_types[1] = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN; strlcpy(banner, _("Password expired. You must change it now."), sizeof(banner)); for (tries = 3; tries; tries--) { TRACE_GIC_PWD_CHANGEPW(context, tries); pw0.length = sizeof(pw0array); pw1.length = sizeof(pw1array); /* PROMPTER_INVOCATION */ krb5int_set_prompt_types(context, prompt_types); ret = (*prompter)(context, data, 0, banner, sizeof(prompt)/sizeof(prompt[0]), prompt); krb5int_set_prompt_types(context, 0); if (ret) goto cleanup; if (strcmp(pw0.data, pw1.data) != 0) { ret = KRB5_LIBOS_BADPWDMATCH; snprintf(banner, sizeof(banner), _("%s. Please try again."), error_message(ret)); } else if (pw0.length == 0) { ret = KRB5_CHPW_PWDNULL; snprintf(banner, sizeof(banner), _("%s. Please try again."), error_message(ret)); } else { int result_code; krb5_data code_string; krb5_data result_string; if ((ret = krb5_change_password(context, &chpw_creds, pw0array, &result_code, &code_string, &result_string))) goto cleanup; /* the change succeeded. go on */ if (result_code == 0) { free(result_string.data); break; } /* set this in case the retry loop falls through */ ret = KRB5_CHPW_FAIL; if (result_code != KRB5_KPASSWD_SOFTERROR) { free(result_string.data); goto cleanup; } /* the error was soft, so try again */ if (krb5_chpw_message(context, &result_string, &message) != 0) message = NULL; /* 100 is I happen to know that no code_string will be longer than 100 chars */ if (message != NULL && strlen(message) > (sizeof(banner) - 100)) message[sizeof(banner) - 100] = '\0'; snprintf(banner, sizeof(banner), _("%.*s%s%s. Please try again.\n"), (int) code_string.length, code_string.data, message ? ": " : "", message ? message : ""); free(message); free(code_string.data); free(result_string.data); } } if (ret) goto cleanup; /* the password change was successful. Get an initial ticket from the master. this is the last try. the return from this is final. */ TRACE_GIC_PWD_CHANGED(context); ret = krb5int_get_init_creds(context, creds, client, prompter, data, start_time, in_tkt_service, options, krb5_get_as_key_password, (void *) &pw0, &use_master, &as_reply); if (ret) goto cleanup; cleanup: if (ret == 0) warn_pw_expiry(context, options, prompter, data, in_tkt_service, as_reply); if (chpw_opts) krb5_get_init_creds_opt_free(context, chpw_opts); memset(pw0array, 0, sizeof(pw0array)); memset(pw1array, 0, sizeof(pw1array)); krb5_free_cred_contents(context, &chpw_creds); if (as_reply) krb5_free_kdc_rep(context, as_reply); return(ret); }