/* * This function allows the caller to supply options to preauth * plugins. Preauth plugin modules are given a chance to look * at each option at the time this function is called in ordre * to check the validity of the option. * The 'opt' pointer supplied to this function must have been * obtained using krb5_get_init_creds_opt_alloc() */ krb5_error_code KRB5_CALLCONV krb5_get_init_creds_opt_set_pa(krb5_context context, krb5_get_init_creds_opt *opt, const char *attr, const char *value) { krb5_error_code retval; krb5_gic_opt_ext *opte; retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, "krb5_get_init_creds_opt_set_pa"); if (retval) return retval; /* * Copy the option into the extended get_init_creds_opt structure */ retval = add_gic_opt_ext_preauth_data(context, opte, attr, value); if (retval) return retval; /* * Give the plugins a chance to look at the option now. */ retval = krb5_preauth_supply_preauth_data(context, opte, attr, value); return retval; }
/* * This function allows a preauth plugin to obtain preauth * options. The preauth_data returned from this function * should be freed by calling krb5_get_init_creds_opt_free_pa(). * * The 'opt' pointer supplied to this function must have been * obtained using krb5_get_init_creds_opt_alloc() */ krb5_error_code KRB5_CALLCONV krb5_get_init_creds_opt_get_pa(krb5_context context, krb5_get_init_creds_opt *opt, int *num_preauth_data, krb5_gic_opt_pa_data **preauth_data) { krb5_error_code retval; krb5_gic_opt_ext *opte; krb5_gic_opt_pa_data *p = NULL; int i; size_t allocsize; retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, "krb5_get_init_creds_opt_get_pa"); if (retval) return retval; if (num_preauth_data == NULL || preauth_data == NULL) return EINVAL; *num_preauth_data = 0; *preauth_data = NULL; if (opte->opt_private->num_preauth_data == 0) return 0; allocsize = opte->opt_private->num_preauth_data * sizeof(krb5_gic_opt_pa_data); p = malloc(allocsize); if (p == NULL) return ENOMEM; /* Init these to make cleanup easier */ for (i = 0; i < opte->opt_private->num_preauth_data; i++) { p[i].attr = NULL; p[i].value = NULL; } for (i = 0; i < opte->opt_private->num_preauth_data; i++) { p[i].attr = strdup(opte->opt_private->preauth_data[i].attr); p[i].value = strdup(opte->opt_private->preauth_data[i].value); if (p[i].attr == NULL || p[i].value == NULL) goto cleanup; } *num_preauth_data = i; *preauth_data = p; return 0; cleanup: for (i = 0; i < opte->opt_private->num_preauth_data; i++) { if (p[i].attr != NULL) free(p[i].attr); if (p[i].value != NULL) free(p[i].value); } free(p); return ENOMEM; }
krb5_error_code KRB5_CALLCONV krb5_get_init_creds_opt_set_fast_flags(krb5_context context, krb5_get_init_creds_opt *opt, krb5_flags flags) { krb5_error_code retval = 0; krb5_gic_opt_ext *opte; retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, "krb5_get_init_creds_opt_set_fast_flags"); if (retval) return retval; opte->opt_private->fast_flags = flags; return retval; }
krb5_error_code KRB5_CALLCONV krb5_get_init_creds_opt_set_out_ccache(krb5_context context, krb5_get_init_creds_opt *opt, krb5_ccache ccache) { krb5_error_code retval = 0; krb5_gic_opt_ext *opte; retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, "krb5_get_init_creds_opt_set_out_ccache"); if (retval) return retval; opte->opt_private->out_ccache = ccache; return 0; }
krb5_error_code KRB5_CALLCONV krb5_get_init_creds_opt_set_responder(krb5_context context, krb5_get_init_creds_opt *opt, krb5_responder_fn responder, void *data) { krb5_error_code ret; krb5_gic_opt_ext *opte; ret = krb5int_gic_opt_to_opte(context, opt, &opte, 0, "krb5_get_init_creds_opt_set_responder"); if (ret) return ret; opte->opt_private->responder = responder; opte->opt_private->responder_data = data; return 0; }
krb5_error_code KRB5_CALLCONV krb5_get_init_creds_opt_set_expire_callback(krb5_context context, krb5_get_init_creds_opt *opt, krb5_expire_callback_func cb, void *data) { krb5_error_code retval = 0; krb5_gic_opt_ext *opte; retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, "krb5_get_init_creds_opt_set_" "expire_callback"); if (retval) return retval; opte->opt_private->expire_cb = cb; opte->opt_private->expire_data = data; return retval; }
krb5_error_code KRB5_CALLCONV krb5_get_init_creds_opt_set_fast_ccache_name(krb5_context context, krb5_get_init_creds_opt *opt, const char *ccache_name) { krb5_error_code retval = 0; krb5_gic_opt_ext *opte; retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0, "krb5_get_init_creds_opt_set_fast_ccache_name"); if (retval) return retval; if (opte->opt_private->fast_ccache_name) { free(opte->opt_private->fast_ccache_name); } opte->opt_private->fast_ccache_name = strdup(ccache_name); if (opte->opt_private->fast_ccache_name == NULL) retval = ENOMEM; return retval; }
/* * Send an appropriate warning prompter if as_reply indicates that the password * is going to expire soon. If an expire callback was provided, use that * instead. */ static void warn_pw_expiry(krb5_context context, krb5_get_init_creds_opt *options, krb5_prompter_fct prompter, void *data, const char *in_tkt_service, krb5_kdc_rep *as_reply) { krb5_error_code ret; krb5_timestamp pw_exp, acct_exp, now; krb5_boolean is_last_req; krb5_deltat delta; krb5_gic_opt_ext *opte; char ts[256], banner[1024]; get_expiry_times(as_reply->enc_part2, &pw_exp, &acct_exp, &is_last_req); ret = krb5int_gic_opt_to_opte(context, options, &opte, 0, ""); if (ret == 0 && opte->opt_private->expire_cb != NULL) { krb5_expire_callback_func cb = opte->opt_private->expire_cb; void *cb_data = opte->opt_private->expire_data; /* Invoke the expire callback and don't send prompter warnings. */ (*cb)(context, cb_data, pw_exp, acct_exp, is_last_req); return; } /* Don't warn if no password expiry value was sent. */ if (pw_exp == 0) return; /* Don't warn if the password is being changed. */ if (in_tkt_service && strcmp(in_tkt_service, "kadmin/changepw") == 0) return; /* * If the expiry time came from a last_req field, assume the KDC wants us * to warn. Otherwise, warn only if the expiry time is less than a week * from now. */ ret = krb5_timeofday(context, &now); if (ret != 0) return; if (!is_last_req && (pw_exp < now || (pw_exp - now) > 7 * 24 * 60 * 60)) return; if (!prompter) return; ret = krb5_timestamp_to_string(pw_exp, ts, sizeof(ts)); if (ret != 0) return; delta = pw_exp - now; if (delta < 3600) { snprintf(banner, sizeof(banner), _("Warning: Your password will expire in less than one hour " "on %s"), ts); } else if (delta < 86400*2) { snprintf(banner, sizeof(banner), _("Warning: Your password will expire in %d hour%s on %s"), delta / 3600, delta < 7200 ? "" : "s", ts); } else { snprintf(banner, sizeof(banner), _("Warning: Your password will expire in %d days on %s"), delta / 86400, ts); } /* PROMPTER_INVOCATION */ (*prompter)(context, data, 0, banner, 0, 0); }
krb5_error_code KRB5_CALLCONV krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_keytab arg_keytab, krb5_deltat start_time, char *in_tkt_service, krb5_get_init_creds_opt *options) { krb5_error_code ret, ret2; int use_master; krb5_keytab keytab; krb5_gic_opt_ext *opte = NULL; if (arg_keytab == NULL) { if ((ret = krb5_kt_default(context, &keytab))) return ret; } else { keytab = arg_keytab; } ret = krb5int_gic_opt_to_opte(context, options, &opte, 1, "krb5_get_init_creds_keytab"); if (ret) return ret; /* * Solaris Kerberos: * If "client" was constructed from krb5_sname_to_princ() it may * have a referral realm. This happens when there is no applicable * domain-to-realm mapping in the Kerberos configuration file. * If that is the case then the realm of the first principal found * in the keytab which matches the client can be used for the client's * realm. */ if (krb5_is_referral_realm(&client->realm)) { krb5_data realm; ret = krb5_kt_find_realm(context, keytab, client, &realm); if (ret == 0) { krb5_free_data_contents(context, &client->realm); client->realm.length = realm.length; client->realm.data = realm.data; } else { /* Try to set a useful error message */ char *princ = NULL; krb5_unparse_name(context, client, &princ); krb5_set_error_message(context, ret, gettext("Failed to find realm for %s in keytab"), princ ? princ : "<unknown>"); if (princ) krb5_free_unparsed_name(context, princ); } } if (ret != 0) goto cleanup; use_master = 0; /* first try: get the requested tkt from any kdc */ ret = krb5_get_init_creds(context, creds, client, NULL, NULL, start_time, in_tkt_service, opte, krb5_get_as_key_keytab, (void *) keytab, &use_master,NULL); /* check for success */ if (ret == 0) goto cleanup; /* If all the kdc's are unavailable fail */ if ((ret == KRB5_KDC_UNREACH) || (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) { use_master = 1; ret2 = krb5_get_init_creds(context, creds, client, NULL, NULL, start_time, in_tkt_service, opte, krb5_get_as_key_keytab, (void *) keytab, &use_master, NULL); if (ret2 == 0) { ret = 0; goto cleanup; } /* if the master is unreachable, return the error from the slave we were able to contact */ if ((ret2 == KRB5_KDC_UNREACH) || (ret2 == KRB5_REALM_CANT_RESOLVE) || (ret2 == KRB5_REALM_UNKNOWN)) goto cleanup; ret = ret2; } /* at this point, we have a response from the master. Since we don't do any prompting or changing for keytabs, that's it. */ cleanup: if (opte && krb5_gic_opt_is_shadowed(opte)) krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte); if (arg_keytab == NULL) (void) krb5_kt_close(context, keytab); /* Solaris Kerberos */ return(ret); }