#include "k5-int.h" #include "int-proto.h" static void init_common(krb5_get_init_creds_opt *opt) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT; } void KRB5_CALLCONV krb5_get_init_creds_opt_init(krb5_get_init_creds_opt *opt) { opt->flags = 0; init_common(opt); } void KRB5_CALLCONV krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt, krb5_deltat tkt_life) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_TKT_LIFE; opt->tkt_life = tkt_life; } void KRB5_CALLCONV krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt *opt, krb5_deltat renew_life) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE; opt->renew_life = renew_life; } void KRB5_CALLCONV krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt *opt, int forwardable) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_FORWARDABLE; opt->forwardable = forwardable; } void KRB5_CALLCONV krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt, int proxiable) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_PROXIABLE; opt->proxiable = proxiable; } void KRB5_CALLCONV krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt, krb5_enctype *etype_list, int etype_list_length) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST; opt->etype_list = etype_list; opt->etype_list_length = etype_list_length; } void KRB5_CALLCONV krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt *opt, krb5_address **addresses) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST; opt->address_list = addresses; } void KRB5_CALLCONV krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt, krb5_preauthtype *preauth_list, int preauth_list_length) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST; opt->preauth_list = preauth_list; opt->preauth_list_length = preauth_list_length; } void KRB5_CALLCONV krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt, krb5_data *salt) { opt->flags |= KRB5_GET_INIT_CREDS_OPT_SALT; opt->salt = salt; } void KRB5_CALLCONV krb5_get_init_creds_opt_set_change_password_prompt(krb5_get_init_creds_opt *opt, int prompt) { if (prompt) opt->flags |= KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT; else opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT; } /* * Extending the krb5_get_init_creds_opt structure. The original * krb5_get_init_creds_opt structure is defined publicly. The * new extended version is private. The original interface * assumed a pre-allocated structure which was passed to * krb5_get_init_creds_init(). The new interface assumes that * the caller will call krb5_get_init_creds_alloc() and * krb5_get_init_creds_free(). * * Callers MUST NOT call krb5_get_init_creds_init() after allocating an * opts structure using krb5_get_init_creds_alloc(). To do so will * introduce memory leaks. Unfortunately, there is no way to enforce * this behavior. * * Two private flags are added for backward compatibility. * KRB5_GET_INIT_CREDS_OPT_EXTENDED says that the structure was allocated * with the new krb5_get_init_creds_opt_alloc() function. * KRB5_GET_INIT_CREDS_OPT_SHADOWED is set to indicate that the extended * structure is a shadow copy of an original krb5_get_init_creds_opt * structure. * If KRB5_GET_INIT_CREDS_OPT_SHADOWED is set after a call to * krb5int_gic_opt_to_opte(), the resulting extended structure should be * freed (using krb5_get_init_creds_free). Otherwise, the original * structure was already extended and there is no need to free it. */ /* Forward prototype */ static void free_gic_opt_ext_preauth_data(krb5_context context, krb5_gic_opt_ext *opte); static krb5_error_code krb5int_gic_opte_private_alloc(krb5_context context, krb5_gic_opt_ext *opte) { if (NULL == opte || !krb5_gic_opt_is_extended(opte)) return EINVAL; opte->opt_private = calloc(1, sizeof(*opte->opt_private)); if (NULL == opte->opt_private) { return ENOMEM; } /* Allocate any private stuff */ opte->opt_private->num_preauth_data = 0; opte->opt_private->preauth_data = NULL; return 0; } static krb5_error_code krb5int_gic_opte_private_free(krb5_context context, krb5_gic_opt_ext *opte) { if (NULL == opte || !krb5_gic_opt_is_extended(opte)) return EINVAL; /* Free up any private stuff */ if (opte->opt_private->preauth_data != NULL) free_gic_opt_ext_preauth_data(context, opte); free(opte->opt_private); opte->opt_private = NULL; return 0; } static krb5_gic_opt_ext * krb5int_gic_opte_alloc(krb5_context context) { krb5_gic_opt_ext *opte; krb5_error_code code; opte = calloc(1, sizeof(*opte)); if (NULL == opte) return NULL; opte->flags = KRB5_GET_INIT_CREDS_OPT_EXTENDED; code = krb5int_gic_opte_private_alloc(context, opte); if (code) { krb5int_set_error(&context->err, code, "krb5int_gic_opte_alloc: krb5int_gic_opte_private_alloc failed"); free(opte); return NULL; } return(opte); } krb5_error_code KRB5_CALLCONV krb5_get_init_creds_opt_alloc(krb5_context context, krb5_get_init_creds_opt **opt) { krb5_gic_opt_ext *opte; if (NULL == opt) return EINVAL; *opt = NULL; /* * We return a new extended structure cast as a krb5_get_init_creds_opt */ opte = krb5int_gic_opte_alloc(context); if (NULL == opte) return ENOMEM; *opt = (krb5_get_init_creds_opt *) opte; init_common(*opt); return 0; } void KRB5_CALLCONV krb5_get_init_creds_opt_free(krb5_context context, krb5_get_init_creds_opt *opt) { krb5_gic_opt_ext *opte; if (NULL == opt) return; /* Don't touch it if we didn't allocate it */ if (!krb5_gic_opt_is_extended(opt)) return; opte = (krb5_gic_opt_ext *)opt; if (opte->opt_private) krb5int_gic_opte_private_free(context, opte); free(opte); } static krb5_error_code krb5int_gic_opte_copy(krb5_context context, krb5_get_init_creds_opt *opt, krb5_gic_opt_ext **opte) { krb5_gic_opt_ext *oe; oe = krb5int_gic_opte_alloc(context); if (NULL == oe) return ENOMEM; if (opt) memcpy(oe, opt, sizeof(*opt)); /* * Fix the flags -- the EXTENDED flag would have been * overwritten by the copy if there was one. The * SHADOWED flag is necessary to ensure that the * krb5_gic_opt_ext structure that was allocated * here will be freed by the library because the * application is unaware of its existence. */ oe->flags |= ( KRB5_GET_INIT_CREDS_OPT_EXTENDED | KRB5_GET_INIT_CREDS_OPT_SHADOWED); *opte = oe; return 0; } /* * Convert a krb5_get_init_creds_opt pointer to a pointer to * an extended, krb5_gic_opt_ext pointer. If the original * pointer already points to an extended structure, then simply * return the original pointer. Otherwise, if 'force' is non-zero, * allocate an extended structure and copy the original over it. * If the original pointer did not point to an extended structure * and 'force' is zero, then return an error. This is used in * cases where the original *should* be an extended structure. */ krb5_error_code krb5int_gic_opt_to_opte(krb5_context context, krb5_get_init_creds_opt *opt, krb5_gic_opt_ext **opte, unsigned int force, const char *where) { if (!krb5_gic_opt_is_extended(opt)) { if (force) { return krb5int_gic_opte_copy(context, opt, opte); } else { krb5int_set_error(&context->err, EINVAL, "%s: attempt to convert non-extended krb5_get_init_creds_opt", where); return EINVAL; } } /* If it is already extended, just return it */ *opte = (krb5_gic_opt_ext *)opt; return 0; } static void free_gic_opt_ext_preauth_data(krb5_context context, krb5_gic_opt_ext *opte) { int i; if (NULL == opte || !krb5_gic_opt_is_extended(opte)) return; if (NULL == opte->opt_private || NULL == opte->opt_private->preauth_data) return; for (i = 0; i < opte->opt_private->num_preauth_data; i++) { if (opte->opt_private->preauth_data[i].attr != NULL) free(opte->opt_private->preauth_data[i].attr); if (opte->opt_private->preauth_data[i].value != NULL) free(opte->opt_private->preauth_data[i].value); } free(opte->opt_private->preauth_data); opte->opt_private->preauth_data = NULL; opte->opt_private->num_preauth_data = 0; } static krb5_error_code add_gic_opt_ext_preauth_data(krb5_context context, krb5_gic_opt_ext *opte, const char *attr, const char *value) { size_t newsize; int i; krb5_gic_opt_pa_data *newpad; newsize = opte->opt_private->num_preauth_data + 1; newsize = newsize * sizeof(*opte->opt_private->preauth_data); if (opte->opt_private->preauth_data == NULL) newpad = malloc(newsize); else newpad = realloc(opte->opt_private->preauth_data, newsize); if (newpad == NULL) return ENOMEM; i = opte->opt_private->num_preauth_data; newpad[i].attr = strdup(attr); if (newpad[i].attr == NULL) return ENOMEM; newpad[i].value = strdup(value); if (newpad[i].value == NULL) { free(newpad[i].attr); return ENOMEM; } opte->opt_private->num_preauth_data += 1; opte->opt_private->preauth_data = newpad; return 0; } /* * 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; } /* * This function frees the preauth_data that was returned by * krb5_get_init_creds_opt_get_pa(). */ void KRB5_CALLCONV krb5_get_init_creds_opt_free_pa(krb5_context context, int num_preauth_data, krb5_gic_opt_pa_data *preauth_data) { int i; if (num_preauth_data <= 0 || preauth_data == NULL) return; for (i = 0; i < num_preauth_data; i++) { if (preauth_data[i].attr != NULL) free(preauth_data[i].attr); if (preauth_data[i].value != NULL) free(preauth_data[i].value); } free(preauth_data); }