Revision 784c38f50e70a739400cdd3f2620bac2e2788e6c authored by Greg Hudson on 01 August 2024, 06:41:15 UTC, committed by Greg Hudson on 05 August 2024, 21:11:01 UTC
Edit the block comment above k5_sendto() to take into account commits 802318cda963456b3ed7856c836e89da891483be (which added request_timeout) and 6436a3808061da787a43c6810f5f0370cdfb6e36 (which made the open TCP connection wait indefinite).
1 parent 2063e72
gic_keytab.c
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* lib/krb5/krb/gic_keytab.c */
/*
* Copyright (C) 2002, 2003, 2008 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*/
#ifndef LEAN_CLIENT
#include "k5-int.h"
#include "int-proto.h"
#include "os-proto.h"
#include "init_creds_ctx.h"
static krb5_error_code
get_as_key_keytab(krb5_context context,
krb5_principal client,
krb5_enctype etype,
krb5_prompter_fct prompter,
void *prompter_data,
krb5_data *salt,
krb5_data *params,
krb5_keyblock *as_key,
void *gak_data,
k5_response_items *ritems)
{
krb5_keytab keytab = (krb5_keytab) gak_data;
krb5_error_code ret;
krb5_keytab_entry kt_ent;
/* We don't need the password from the responder to create the AS key. */
if (as_key == NULL)
return 0;
/* if there's already a key of the correct etype, we're done.
if the etype is wrong, free the existing key, and make
a new one. */
if (as_key->length) {
if (as_key->enctype == etype)
return(0);
krb5_free_keyblock_contents(context, as_key);
as_key->length = 0;
}
if (!krb5_c_valid_enctype(etype))
return(KRB5_PROG_ETYPE_NOSUPP);
if ((ret = krb5_kt_get_entry(context, keytab, client,
0, /* don't have vno available */
etype, &kt_ent)))
return(ret);
/* Steal the keyblock from kt_ent for the caller. */
*as_key = kt_ent.key;
memset(&kt_ent.key, 0, sizeof(kt_ent.key));
(void) krb5_kt_free_entry(context, &kt_ent);
return 0;
}
/* Return the list of etypes available for client in keytab. */
static krb5_error_code
lookup_etypes_for_keytab(krb5_context context, krb5_keytab keytab,
krb5_const_principal client,
krb5_enctype **etypes_out)
{
krb5_kt_cursor cursor;
krb5_keytab_entry entry;
krb5_enctype *p, *etypes = NULL, etype;
krb5_kvno max_kvno = 0, vno;
krb5_error_code ret;
krb5_boolean match;
size_t count = 0;
*etypes_out = NULL;
if (keytab->ops->start_seq_get == NULL)
return EINVAL;
ret = krb5_kt_start_seq_get(context, keytab, &cursor);
if (ret != 0)
return ret;
while (!(ret = krb5_kt_next_entry(context, keytab, &entry, &cursor))) {
/* Extract what we need from the entry and free it. */
etype = entry.key.enctype;
vno = entry.vno;
match = krb5_principal_compare(context, entry.principal, client);
krb5_free_keytab_entry_contents(context, &entry);
/* Filter out old or non-matching entries and invalid enctypes. */
if (vno < max_kvno || !match || !krb5_c_valid_enctype(etype))
continue;
/* Update max_kvno and reset the list if we find a newer kvno. */
if (vno > max_kvno) {
max_kvno = vno;
free(etypes);
etypes = NULL;
count = 0;
}
/* Leave room for the terminator and possibly a second entry. */
p = realloc(etypes, (count + 3) * sizeof(*etypes));
if (p == NULL) {
ret = ENOMEM;
goto cleanup;
}
etypes = p;
etypes[count++] = etype;
etypes[count] = 0;
}
if (ret != KRB5_KT_END)
goto cleanup;
ret = 0;
*etypes_out = etypes;
etypes = NULL;
cleanup:
krb5_kt_end_seq_get(context, keytab, &cursor);
free(etypes);
return ret;
}
/* Move the entries in keytab_list (zero-terminated) to the front of req_list
* (of length req_len), preserving order otherwise. */
static krb5_error_code
sort_enctypes(krb5_enctype *req_list, int req_len, krb5_enctype *keytab_list)
{
krb5_enctype *save_list;
int save_pos, req_pos, i;
save_list = malloc(req_len * sizeof(*save_list));
if (save_list == NULL)
return ENOMEM;
/* Sort req_list entries into the front of req_list or into save_list. */
req_pos = save_pos = 0;
for (i = 0; i < req_len; i++) {
if (k5_etypes_contains(keytab_list, req_list[i]))
req_list[req_pos++] = req_list[i];
else
save_list[save_pos++] = req_list[i];
}
/* Put the entries we saved back in at the end, in order. */
for (i = 0; i < save_pos; i++)
req_list[req_pos++] = save_list[i];
assert(req_pos == req_len);
free(save_list);
return 0;
}
krb5_error_code KRB5_CALLCONV
krb5_init_creds_set_keytab(krb5_context context,
krb5_init_creds_context ctx,
krb5_keytab keytab)
{
krb5_enctype *etype_list = NULL;
krb5_error_code ret;
struct canonprinc iter = { ctx->request->client, .subst_defrealm = TRUE };
krb5_const_principal canonprinc;
krb5_principal copy;
char *name;
ctx->gak_fct = get_as_key_keytab;
ctx->gak_data = keytab;
/* We may be authenticating as a host-based principal. If so, look for
* each canonicalization candidate in the keytab. */
while ((ret = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
canonprinc != NULL) {
ret = lookup_etypes_for_keytab(context, keytab, canonprinc,
&etype_list);
if (ret || etype_list != NULL)
break;
}
if (!ret && canonprinc != NULL) {
/* Authenticate as the principal we found in the keytab. */
ret = krb5_copy_principal(context, canonprinc, &copy);
if (!ret) {
krb5_free_principal(context, ctx->request->client);
ctx->request->client = copy;
}
}
free_canonprinc(&iter);
if (ret) {
TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED(context, ret);
free(etype_list);
return 0;
}
TRACE_INIT_CREDS_KEYTAB_LOOKUP(context, ctx->request->client, etype_list);
/* Error out if we have no keys for the client principal. */
if (etype_list == NULL) {
ret = krb5_unparse_name(context, ctx->request->client, &name);
if (ret == 0) {
k5_setmsg(context, KRB5_KT_NOTFOUND,
_("Keytab contains no suitable keys for %s"), name);
}
krb5_free_unparsed_name(context, name);
return KRB5_KT_NOTFOUND;
}
/* Sort the request enctypes so the ones in the keytab appear first. */
ret = sort_enctypes(ctx->request->ktype, ctx->request->nktypes,
etype_list);
free(etype_list);
return ret;
}
static krb5_error_code
get_init_creds_keytab(krb5_context context, krb5_creds *creds,
krb5_principal client, krb5_keytab keytab,
krb5_deltat start_time, const char *in_tkt_service,
krb5_get_init_creds_opt *options,
krb5_boolean use_primary, struct kdclist *kdcs)
{
krb5_error_code ret;
krb5_init_creds_context ctx = NULL;
ret = krb5_init_creds_init(context, client, NULL, NULL, start_time,
options, &ctx);
if (ret != 0)
goto cleanup;
if (in_tkt_service) {
ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
if (ret != 0)
goto cleanup;
}
ret = krb5_init_creds_set_keytab(context, ctx, keytab);
if (ret != 0)
goto cleanup;
ret = k5_init_creds_get(context, ctx, use_primary, kdcs);
if (ret != 0)
goto cleanup;
ret = krb5_init_creds_get_creds(context, ctx, creds);
if (ret != 0)
goto cleanup;
cleanup:
krb5_init_creds_free(context, ctx);
return ret;
}
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,
const char *in_tkt_service,
krb5_get_init_creds_opt *options)
{
krb5_error_code ret;
krb5_keytab keytab;
struct errinfo errsave = EMPTY_ERRINFO;
struct kdclist *kdcs = NULL;
if (arg_keytab == NULL) {
if ((ret = krb5_kt_default(context, &keytab)))
return ret;
} else {
keytab = arg_keytab;
}
ret = k5_kdclist_create(&kdcs);
if (ret)
goto cleanup;
/* first try: get the requested tkt from any kdc */
ret = get_init_creds_keytab(context, creds, client, keytab, start_time,
in_tkt_service, options, FALSE, kdcs);
/* 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 primary kdc, try again with
* the primary kdc. */
if (!k5_kdclist_any_replicas(context, kdcs)) {
k5_save_ctx_error(context, ret, &errsave);
ret = get_init_creds_keytab(context, creds, client, keytab,
start_time, in_tkt_service, options,
TRUE, NULL);
if (ret == 0)
goto cleanup;
/* If the primary is unreachable, return the error from the replica we
* were able to contact. */
if (ret == KRB5_KDC_UNREACH || ret == KRB5_REALM_CANT_RESOLVE ||
ret == KRB5_REALM_UNKNOWN)
ret = k5_restore_ctx_error(context, &errsave);
}
/* at this point, we have a response from the primary. Since we don't
do any prompting or changing for keytabs, that's it. */
cleanup:
if (arg_keytab == NULL)
krb5_kt_close(context, keytab);
k5_kdclist_free(kdcs);
k5_clear_error(&errsave);
return(ret);
}
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;
retval = k5_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 = k5_get_init_creds(context, creds, creds->client,
krb5_prompter_posix, NULL, 0, server, opts,
get_as_key_keytab, (void *)keytab,
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;
}
#endif /* LEAN_CLIENT */

Computing file changes ...