2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/krb5/krb/gic_keytab.c
2N/A *
2N/A * Copyright (C) 2002, 2003, 2008 by the Massachusetts Institute of Technology.
2N/A * All rights reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A#ifndef LEAN_CLIENT
2N/A
2N/A#include "k5-int.h"
2N/A#include "init_creds_ctx.h"
2N/A/* Solaris Kerberos */
2N/A#include <libintl.h>
2N/A#include <locale.h>
2N/A
2N/Astatic krb5_error_code
2N/Aget_as_key_keytab(krb5_context context,
2N/A krb5_principal client,
2N/A krb5_enctype etype,
2N/A krb5_prompter_fct prompter,
2N/A void *prompter_data,
2N/A krb5_data *salt,
2N/A krb5_data *params,
2N/A krb5_keyblock *as_key,
2N/A void *gak_data)
2N/A{
2N/A krb5_keytab keytab = (krb5_keytab) gak_data;
2N/A krb5_error_code ret;
2N/A krb5_keytab_entry kt_ent;
2N/A krb5_keyblock *kt_key;
2N/A
2N/A /* if there's already a key of the correct etype, we're done.
2N/A if the etype is wrong, free the existing key, and make
2N/A a new one. */
2N/A
2N/A if (as_key->length) {
2N/A if (as_key->enctype == etype)
2N/A return(0);
2N/A
2N/A krb5_free_keyblock_contents(context, as_key);
2N/A as_key->length = 0;
2N/A }
2N/A
2N/A if (!krb5_c_valid_enctype(etype))
2N/A return(KRB5_PROG_ETYPE_NOSUPP);
2N/A
2N/A if ((ret = krb5_kt_get_entry(context, keytab, client,
2N/A 0, /* don't have vno available */
2N/A etype, &kt_ent)))
2N/A return(ret);
2N/A
2N/A ret = krb5_copy_keyblock(context, &kt_ent.key, &kt_key);
2N/A
2N/A /* again, krb5's memory management is lame... */
2N/A
2N/A *as_key = *kt_key;
2N/A free(kt_key);
2N/A
2N/A (void) krb5_kt_free_entry(context, &kt_ent);
2N/A
2N/A return(ret);
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_init_creds_set_keytab(krb5_context context,
2N/A krb5_init_creds_context ctx,
2N/A krb5_keytab keytab)
2N/A{
2N/A ctx->gak_fct = get_as_key_keytab;
2N/A ctx->gak_data = keytab;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_get_init_creds_keytab(krb5_context context,
2N/A krb5_creds *creds,
2N/A krb5_principal client,
2N/A krb5_keytab arg_keytab,
2N/A krb5_deltat start_time,
2N/A char *in_tkt_service,
2N/A krb5_get_init_creds_opt *options)
2N/A{
2N/A krb5_error_code ret, ret2;
2N/A int use_master;
2N/A krb5_keytab keytab;
2N/A /* Solaris Kerberos */
2N/A const char *err_msg = NULL;
2N/A
2N/A if (arg_keytab == NULL) {
2N/A if ((ret = krb5_kt_default(context, &keytab)))
2N/A return ret;
2N/A } else {
2N/A keytab = arg_keytab;
2N/A }
2N/A
2N/A /*
2N/A * Solaris Kerberos:
2N/A * If "client" was constructed from krb5_sname_to_princ() it may
2N/A * have a referral realm. This happens when there is no applicable
2N/A * domain-to-realm mapping in the Kerberos configuration file.
2N/A * If that is the case then the realm of the first principal found
2N/A * in the keytab which matches the client can be used for the client's
2N/A * realm.
2N/A */
2N/A if (krb5_is_referral_realm(&client->realm)) {
2N/A krb5_data realm;
2N/A ret = krb5_kt_find_realm(context, keytab, client, &realm);
2N/A if (ret == 0) {
2N/A krb5_free_data_contents(context, &client->realm);
2N/A client->realm.length = realm.length;
2N/A client->realm.data = realm.data;
2N/A } else {
2N/A /* Try to set a useful error message */
2N/A char *princ = NULL;
2N/A krb5_unparse_name(context, client, &princ);
2N/A
2N/A krb5_set_error_message(context, ret,
2N/A gettext("Failed to find realm for %s in "
2N/A "keytab"),
2N/A princ ? princ : "<unknown>");
2N/A if (princ)
2N/A krb5_free_unparsed_name(context, princ);
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A use_master = 0;
2N/A
2N/A /* first try: get the requested tkt from any kdc */
2N/A
2N/A ret = krb5int_get_init_creds(context, creds, client, NULL, NULL,
2N/A start_time, in_tkt_service, options,
2N/A get_as_key_keytab, (void *) keytab,
2N/A &use_master,NULL);
2N/A
2N/A /* check for success */
2N/A
2N/A if (ret == 0)
2N/A goto cleanup;
2N/A
2N/A /* If all the kdc's are unavailable fail */
2N/A
2N/A if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE))
2N/A goto cleanup;
2N/A
2N/A /* if the reply did not come from the master kdc, try again with
2N/A the master kdc */
2N/A
2N/A if (!use_master) {
2N/A use_master = 1;
2N/A
2N/A /* Solaris Kerberos - save the original error message string */
2N/A err_msg = krb5_get_error_message(context, ret);
2N/A
2N/A ret2 = krb5int_get_init_creds(context, creds, client, NULL, NULL,
2N/A start_time, in_tkt_service, options,
2N/A get_as_key_keytab, (void *) keytab,
2N/A &use_master, NULL);
2N/A
2N/A if (ret2 == 0) {
2N/A ret = 0;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* if the master is unreachable, return the error from the
2N/A slave we were able to contact */
2N/A
2N/A if ((ret2 == KRB5_KDC_UNREACH) ||
2N/A (ret2 == KRB5_REALM_CANT_RESOLVE) ||
2N/A (ret2 == KRB5_REALM_UNKNOWN)) {
2N/A
2N/A /* Solaris Kerberos - restore the original error message string */
2N/A krb5_set_error_message(context, ret, err_msg);
2N/A
2N/A goto cleanup;
2N/A }
2N/A
2N/A ret = ret2;
2N/A }
2N/A
2N/A /* at this point, we have a response from the master. Since we don't
2N/A do any prompting or changing for keytabs, that's it. */
2N/A
2N/Acleanup:
2N/A /* Solaris Kerberos */
2N/A if (err_msg)
2N/A krb5_free_error_message(context, err_msg);
2N/A
2N/A if (arg_keytab == NULL)
2N/A krb5_kt_close(context, keytab);
2N/A
2N/A return(ret);
2N/A}
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options,
2N/A krb5_address *const *addrs, krb5_enctype *ktypes,
2N/A krb5_preauthtype *pre_auth_types,
2N/A krb5_keytab arg_keytab, krb5_ccache ccache,
2N/A krb5_creds *creds, krb5_kdc_rep **ret_as_reply)
2N/A{
2N/A krb5_error_code retval;
2N/A krb5_get_init_creds_opt *opts;
2N/A char * server = NULL;
2N/A krb5_keytab keytab;
2N/A krb5_principal client_princ, server_princ;
2N/A int use_master = 0;
2N/A
2N/A retval = krb5int_populate_gic_opt(context, &opts,
2N/A options, addrs, ktypes,
2N/A pre_auth_types, creds);
2N/A if (retval)
2N/A return retval;
2N/A
2N/A if (arg_keytab == NULL) {
2N/A retval = krb5_kt_default(context, &keytab);
2N/A if (retval)
2N/A goto cleanup;
2N/A }
2N/A else keytab = arg_keytab;
2N/A
2N/A retval = krb5_unparse_name( context, creds->server, &server);
2N/A if (retval)
2N/A goto cleanup;
2N/A server_princ = creds->server;
2N/A client_princ = creds->client;
2N/A retval = krb5int_get_init_creds(context, creds, creds->client,
2N/A krb5_prompter_posix, NULL,
2N/A 0, server, opts,
2N/A get_as_key_keytab, (void *)keytab,
2N/A &use_master, ret_as_reply);
2N/A krb5_free_unparsed_name( context, server);
2N/A if (retval) {
2N/A goto cleanup;
2N/A }
2N/A krb5_free_principal(context, creds->server);
2N/A krb5_free_principal(context, creds->client);
2N/A creds->client = client_princ;
2N/A creds->server = server_princ;
2N/A
2N/A /* store it in the ccache! */
2N/A if (ccache)
2N/A if ((retval = krb5_cc_store_cred(context, ccache, creds)))
2N/A goto cleanup;
2N/Acleanup:
2N/A krb5_get_init_creds_opt_free(context, opts);
2N/A if (arg_keytab == NULL)
2N/A krb5_kt_close(context, keytab);
2N/A return retval;
2N/A}
2N/A
2N/A#endif /* LEAN_CLIENT */