Compare commits

...

15 Commits

Author SHA1 Message Date
7466da837b import UBI krb5-1.18.2-31.el8_10 2025-03-13 11:47:50 +00:00
fc51be7bd6 import UBI krb5-1.18.2-30.el8_10 2024-11-05 01:49:18 +00:00
d29ac3a0b1 import UBI krb5-1.18.2-29.el8_10 2024-08-13 19:19:58 +00:00
d760d9c2dc import UBI krb5-1.18.2-28.el8_10 2024-07-02 19:43:58 +00:00
81bd838f78 import UBI krb5-1.18.2-27.el8_10 2024-05-22 14:50:30 +00:00
eabdullin
72f8fad7b9 import UBI krb5-1.18.2-26.el8_9 2023-11-14 23:10:42 +00:00
6d26dc1d96 import krb5-1.18.2-25.el8_8 2023-06-29 20:08:51 +00:00
CentOS Sources
b3034f73a2 import krb5-1.18.2-22.el8_7 2022-11-28 09:22:34 +00:00
CentOS Sources
978a4ade58 import krb5-1.18.2-21.el8 2022-11-08 13:14:02 +00:00
CentOS Sources
0314e20873 import krb5-1.18.2-14.el8 2021-11-09 10:05:00 +00:00
CentOS Sources
475028c8e9 import krb5-1.18.2-8.3.el8_4 2021-09-22 09:06:56 +00:00
CentOS Sources
8d2db90722 import krb5-1.18.2-8.el8 2021-09-09 19:45:35 +00:00
CentOS Sources
ee1d0c5e34 import krb5-1.18.2-5.el8 2021-09-09 19:45:32 +00:00
CentOS Sources
3ec284c784 import krb5-1.17-18.el8 2021-09-09 19:45:29 +00:00
CentOS Sources
8fbeb381dc import krb5-1.17-9.el8 2021-09-09 19:45:26 +00:00
124 changed files with 20107 additions and 27558 deletions

4
.gitignore vendored
View File

@ -1,2 +1,2 @@
SOURCES/krb5-1.16.1-pdfs.tar
SOURCES/krb5-1.16.1.tar.gz
SOURCES/krb5-1.18.2-pdfs.tar
SOURCES/krb5-1.18.2.tar.gz

View File

@ -1,2 +1,2 @@
494c62bea08e5d26e01d47c409ac745b65e509c8 SOURCES/krb5-1.16.1-pdfs.tar
8353f2d900a7d52499c7c2605d5e295f71dd5e67 SOURCES/krb5-1.16.1.tar.gz
db930a6653503c36027a4f65d761f8838c7636ae SOURCES/krb5-1.18.2-pdfs.tar
547c4e4afa06dd39c888a9ee89397ec3c3425c90 SOURCES/krb5-1.18.2.tar.gz

View File

@ -0,0 +1,222 @@
From de01999b35773196749ba714f233649c9528aaad Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 14 Jan 2021 18:13:09 -0500
Subject: [PATCH] Add APIs for marshalling credentials
Faciliate KCM daemon implementations by providing functions to
deserialize and reserialize credentials in the FILE v4 format.
[ghudson@mit.edu: minor editorial changes]
ticket: 8980 (new)
(cherry picked from commit 18ea3bd2fca55b789b7de9c663624bc11d348fa6)
(cherry picked from commit 3d11179707923b033fa413387a33296b673ff52d)
[rharwood@redhat.com: function backport, so conflict in krb5_32.def]
---
doc/appdev/refs/api/index.rst | 2 ++
src/include/krb5/krb5.hin | 36 ++++++++++++++++++++++
src/lib/krb5/ccache/ccmarshal.c | 53 +++++++++++++++++++++++++++++++++
src/lib/krb5/ccache/t_marshal.c | 15 +++++++++-
src/lib/krb5/libkrb5.exports | 2 ++
src/lib/krb5_32.def | 4 +++
6 files changed, 111 insertions(+), 1 deletion(-)
diff --git a/doc/appdev/refs/api/index.rst b/doc/appdev/refs/api/index.rst
index 727d9b492..9e03fd386 100644
--- a/doc/appdev/refs/api/index.rst
+++ b/doc/appdev/refs/api/index.rst
@@ -232,6 +232,7 @@ Rarely used public interfaces
krb5_kt_remove_entry.rst
krb5_kt_start_seq_get.rst
krb5_make_authdata_kdc_issued.rst
+ krb5_marshal_credentials.rst
krb5_merge_authdata.rst
krb5_mk_1cred.rst
krb5_mk_error.rst
@@ -285,6 +286,7 @@ Rarely used public interfaces
krb5_tkt_creds_get_times.rst
krb5_tkt_creds_init.rst
krb5_tkt_creds_step.rst
+ krb5_unmarshal_credentials.rst
krb5_verify_init_creds.rst
krb5_verify_init_creds_opt_init.rst
krb5_verify_init_creds_opt_set_ap_req_nofail.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 9264bede1..d2cf1eba2 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -3125,6 +3125,42 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
krb5_ccache ccache, krb5_creds *in_creds,
krb5_creds **out_creds);
+/**
+ * Serialize a @c krb5_creds object.
+ *
+ * @param [in] context Library context
+ * @param [in] creds The credentials object to serialize
+ * @param [out] data_out The serialized credentials
+ *
+ * Serialize @a creds in the format used by the FILE ccache format (vesion 4)
+ * and KCM ccache protocol.
+ *
+ * Use krb5_free_data() to free @a data_out when it is no longer needed.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_marshal_credentials(krb5_context context, krb5_creds *in_creds,
+ krb5_data **data_out);
+
+/**
+ * Deserialize a @c krb5_creds object.
+ *
+ * @param [in] context Library context
+ * @param [in] data The serialized credentials
+ * @param [out] creds_out The resulting creds object
+ *
+ * Deserialize @a data to credentials in the format used by the FILE ccache
+ * format (vesion 4) and KCM ccache protocol.
+ *
+ * Use krb5_free_creds() to free @a creds_out when it is no longer needed.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_unmarshal_credentials(krb5_context context, const krb5_data *data,
+ krb5_creds **creds_out);
+
/** @deprecated Replaced by krb5_get_validated_creds. */
krb5_error_code KRB5_CALLCONV
krb5_get_credentials_validate(krb5_context context, krb5_flags options,
diff --git a/src/lib/krb5/ccache/ccmarshal.c b/src/lib/krb5/ccache/ccmarshal.c
index ae634ccab..ab284e721 100644
--- a/src/lib/krb5/ccache/ccmarshal.c
+++ b/src/lib/krb5/ccache/ccmarshal.c
@@ -515,3 +515,56 @@ k5_marshal_mcred(struct k5buf *buf, krb5_creds *mcred)
if (mcred->second_ticket.length > 0)
put_data(buf, version, &mcred->second_ticket);
}
+
+krb5_error_code KRB5_CALLCONV
+krb5_marshal_credentials(krb5_context context, krb5_creds *in_creds,
+ krb5_data **data_out)
+{
+ krb5_error_code ret;
+ krb5_data *data;
+ struct k5buf buf;
+
+ *data_out = NULL;
+
+ data = k5alloc(sizeof(krb5_data), &ret);
+ if (ret)
+ return ret;
+
+ k5_buf_init_dynamic(&buf);
+ k5_marshal_cred(&buf, 4, in_creds);
+
+ ret = k5_buf_status(&buf);
+ if (ret) {
+ free(data);
+ return ret;
+ }
+
+ /* Steal payload from buf. */
+ *data = make_data(buf.data, buf.len);
+ *data_out = data;
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_unmarshal_credentials(krb5_context context, const krb5_data *data,
+ krb5_creds **creds_out)
+{
+ krb5_error_code ret;
+ krb5_creds *creds;
+
+ *creds_out = NULL;
+
+ creds = k5alloc(sizeof(krb5_creds), &ret);
+ if (ret)
+ return ret;
+
+ ret = k5_unmarshal_cred((unsigned char *)data->data, data->length, 4,
+ creds);
+ if (ret) {
+ free(creds);
+ return ret;
+ }
+
+ *creds_out = creds;
+ return 0;
+}
diff --git a/src/lib/krb5/ccache/t_marshal.c b/src/lib/krb5/ccache/t_marshal.c
index 144554c30..47ec2e94d 100644
--- a/src/lib/krb5/ccache/t_marshal.c
+++ b/src/lib/krb5/ccache/t_marshal.c
@@ -268,13 +268,14 @@ main(int argc, char **argv)
krb5_context context;
krb5_ccache cache;
krb5_principal princ;
- krb5_creds cred1, cred2;
+ krb5_creds cred1, cred2, *alloc_cred;
krb5_cc_cursor cursor;
const char *filename;
char *ccname, filebuf[256];
int version, fd;
const struct test *t;
struct k5buf buf;
+ krb5_data ser_data, *alloc_data;
if (argc != 2)
abort();
@@ -285,6 +286,18 @@ main(int argc, char **argv)
if (krb5_init_context(&context) != 0)
abort();
+ /* Test public functions for unmarshalling and marshalling. */
+ ser_data = make_data((char *)tests[3].cred1, tests[3].cred1len);
+ if (krb5_unmarshal_credentials(context, &ser_data, &alloc_cred) != 0)
+ abort();
+ verify_cred1(alloc_cred);
+ if (krb5_marshal_credentials(context, alloc_cred, &alloc_data) != 0)
+ abort();
+ assert(alloc_data->length == tests[3].cred1len);
+ assert(memcmp(tests[3].cred1, alloc_data->data, alloc_data->length) == 0);
+ krb5_free_data(context, alloc_data);
+ krb5_free_creds(context, alloc_cred);
+
for (version = FIRST_VERSION; version <= 4; version++) {
t = &tests[version - 1];
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index cab5b3b17..48ae46f5c 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -488,6 +488,7 @@ krb5_lock_file
krb5_make_authdata_kdc_issued
krb5_make_full_ipaddr
krb5_make_fulladdr
+krb5_marshal_credentials
krb5_mcc_ops
krb5_merge_authdata
krb5_mk_1cred
@@ -592,6 +593,7 @@ krb5_timeofday
krb5_timestamp_to_sfstring
krb5_timestamp_to_string
krb5_unlock_file
+krb5_unmarshal_credentials
krb5_unpack_full_ipaddr
krb5_unparse_name
krb5_unparse_name_ext
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
index de5823c17..209c6aaef 100644
--- a/src/lib/krb5_32.def
+++ b/src/lib/krb5_32.def
@@ -502,3 +502,7 @@ EXPORTS
; new in 1.19
k5_cc_store_primary_cred @470 ; PRIVATE
+
+; new in 1.20
+ krb5_marshal_credentials @472
+ krb5_unmarshal_credentials @473

View File

@ -1,866 +0,0 @@
From dff5177801444307d19071fc4fac7de864fda92a Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sat, 13 Jun 2015 16:04:53 -0400
Subject: [PATCH] Add ASN.1 encoders and decoders for SPAKE types
Add a new internal header k5-spake.h. Add ASN.1 encoder and decoder
functions and an internal free function for SPAKE types. Add ASN.1
tests and asn1c test vectors the new types.
The additions to to make-vectors.c use C99 designated initializers in
order to initialize unions. This is okay since make-vectors.c is only
compiled as part of "make test-vectors" and not as part of the regular
build.
(cherry picked from commit 78a09d95dff6915da4079bc611f4bb95f6a95f70)
---
src/include/k5-spake.h | 107 +++++++++++++++++++++++++++
src/lib/krb5/asn.1/asn1_k_encode.c | 52 ++++++++++++-
src/lib/krb5/krb/kfree.c | 40 ++++++++++
src/lib/krb5/libkrb5.exports | 6 ++
src/tests/asn.1/Makefile.in | 2 +-
src/tests/asn.1/krb5_decode_test.c | 37 +++++++++
src/tests/asn.1/krb5_encode_test.c | 29 ++++++++
src/tests/asn.1/ktest.c | 97 ++++++++++++++++++++++++
src/tests/asn.1/ktest.h | 9 +++
src/tests/asn.1/ktest_equal.c | 49 ++++++++++++
src/tests/asn.1/ktest_equal.h | 6 ++
src/tests/asn.1/make-vectors.c | 56 ++++++++++++++
src/tests/asn.1/reference_encode.out | 6 ++
src/tests/asn.1/spake.asn1 | 44 +++++++++++
src/tests/asn.1/trval_reference.out | 50 +++++++++++++
15 files changed, 588 insertions(+), 2 deletions(-)
create mode 100644 src/include/k5-spake.h
create mode 100644 src/tests/asn.1/spake.asn1
diff --git a/src/include/k5-spake.h b/src/include/k5-spake.h
new file mode 100644
index 000000000..ddb5d810d
--- /dev/null
+++ b/src/include/k5-spake.h
@@ -0,0 +1,107 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-spake.h - SPAKE preauth mech declarations */
+/*
+ * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * The SPAKE preauth mechanism allows long-term client keys to be used for
+ * preauthentication without exposing them to offline dictionary attacks. The
+ * negotiated key can also be used for second-factor authentication. This
+ * header file declares structures and encoder/decoder functions for the
+ * mechanism's padata messages.
+ */
+
+#ifndef K5_SPAKE_H
+#define K5_SPAKE_H
+
+#include "k5-int.h"
+
+/* SPAKESecondFactor is contained within a SPAKEChallenge, SPAKEResponse, or
+ * EncryptedData message and contains a second-factor challenge or response. */
+typedef struct krb5_spake_factor_st {
+ int32_t type;
+ krb5_data *data;
+} krb5_spake_factor;
+
+/* SPAKESupport is sent from the client to the KDC to indicate which group the
+ * client supports. */
+typedef struct krb5_spake_support_st {
+ int32_t ngroups;
+ int32_t *groups;
+} krb5_spake_support;
+
+/* SPAKEChallenge is sent from the KDC to the client to communicate its group
+ * selection, public value, and second-factor challenge options. */
+typedef struct krb5_spake_challenge_st {
+ int32_t group;
+ krb5_data pubkey;
+ krb5_spake_factor **factors;
+} krb5_spake_challenge;
+
+/* SPAKEResponse is sent from the client to the KDC to communicate its public
+ * value and encrypted second-factor response. */
+typedef struct krb5_spake_response_st {
+ krb5_data pubkey;
+ krb5_enc_data factor;
+} krb5_spake_response;
+
+enum krb5_spake_msgtype {
+ SPAKE_MSGTYPE_UNKNOWN = -1,
+ SPAKE_MSGTYPE_SUPPORT = 0,
+ SPAKE_MSGTYPE_CHALLENGE = 1,
+ SPAKE_MSGTYPE_RESPONSE = 2,
+ SPAKE_MSGTYPE_ENCDATA = 3
+};
+
+/* PA-SPAKE is a choice among the message types which can appear in a PA-SPAKE
+ * padata element. */
+typedef struct krb5_pa_spake_st {
+ enum krb5_spake_msgtype choice;
+ union krb5_spake_message_choices {
+ krb5_spake_support support;
+ krb5_spake_challenge challenge;
+ krb5_spake_response response;
+ krb5_enc_data encdata;
+ } u;
+} krb5_pa_spake;
+
+krb5_error_code encode_krb5_spake_factor(const krb5_spake_factor *val,
+ krb5_data **code_out);
+krb5_error_code decode_krb5_spake_factor(const krb5_data *code,
+ krb5_spake_factor **val_out);
+void k5_free_spake_factor(krb5_context context, krb5_spake_factor *val);
+
+krb5_error_code encode_krb5_pa_spake(const krb5_pa_spake *val,
+ krb5_data **code_out);
+krb5_error_code decode_krb5_pa_spake(const krb5_data *code,
+ krb5_pa_spake **val_out);
+void k5_free_pa_spake(krb5_context context, krb5_pa_spake *val);
+
+#endif /* K5_SPAKE_H */
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index 3b23fe34a..29f6b903d 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -25,7 +25,7 @@
*/
#include "asn1_encode.h"
-#include <assert.h>
+#include "k5-spake.h"
DEFINT_IMMEDIATE(krb5_version, KVNO, KRB5KDC_ERR_BAD_PVNO);
@@ -1817,3 +1817,53 @@ static const struct atype_info *secure_cookie_fields[] = {
DEFSEQTYPE(secure_cookie, krb5_secure_cookie, secure_cookie_fields);
MAKE_ENCODER(encode_krb5_secure_cookie, secure_cookie);
MAKE_DECODER(decode_krb5_secure_cookie, secure_cookie);
+
+DEFFIELD(spake_factor_0, krb5_spake_factor, type, 0, int32);
+DEFFIELD(spake_factor_1, krb5_spake_factor, data, 1, opt_ostring_data_ptr);
+static const struct atype_info *spake_factor_fields[] = {
+ &k5_atype_spake_factor_0, &k5_atype_spake_factor_1
+};
+DEFSEQTYPE(spake_factor, krb5_spake_factor, spake_factor_fields);
+DEFPTRTYPE(spake_factor_ptr, spake_factor);
+DEFNULLTERMSEQOFTYPE(seqof_spake_factor, spake_factor_ptr);
+DEFPTRTYPE(ptr_seqof_spake_factor, seqof_spake_factor);
+MAKE_ENCODER(encode_krb5_spake_factor, spake_factor);
+MAKE_DECODER(decode_krb5_spake_factor, spake_factor);
+
+DEFCNFIELD(spake_support_0, krb5_spake_support, groups, ngroups, 0,
+ cseqof_int32);
+static const struct atype_info *spake_support_fields[] = {
+ &k5_atype_spake_support_0
+};
+DEFSEQTYPE(spake_support, krb5_spake_support, spake_support_fields);
+
+DEFFIELD(spake_challenge_0, krb5_spake_challenge, group, 0, int32);
+DEFFIELD(spake_challenge_1, krb5_spake_challenge, pubkey, 1, ostring_data);
+DEFFIELD(spake_challenge_2, krb5_spake_challenge, factors, 2,
+ ptr_seqof_spake_factor);
+static const struct atype_info *spake_challenge_fields[] = {
+ &k5_atype_spake_challenge_0, &k5_atype_spake_challenge_1,
+ &k5_atype_spake_challenge_2
+};
+DEFSEQTYPE(spake_challenge, krb5_spake_challenge, spake_challenge_fields);
+
+DEFFIELD(spake_response_0, krb5_spake_response, pubkey, 0, ostring_data);
+DEFFIELD(spake_response_1, krb5_spake_response, factor, 1, encrypted_data);
+static const struct atype_info *spake_response_fields[] = {
+ &k5_atype_spake_response_0, &k5_atype_spake_response_1,
+};
+DEFSEQTYPE(spake_response, krb5_spake_response, spake_response_fields);
+
+DEFCTAGGEDTYPE(pa_spake_0, 0, spake_support);
+DEFCTAGGEDTYPE(pa_spake_1, 1, spake_challenge);
+DEFCTAGGEDTYPE(pa_spake_2, 2, spake_response);
+DEFCTAGGEDTYPE(pa_spake_3, 3, encrypted_data);
+static const struct atype_info *pa_spake_alternatives[] = {
+ &k5_atype_pa_spake_0, &k5_atype_pa_spake_1, &k5_atype_pa_spake_2,
+ &k5_atype_pa_spake_3
+};
+DEFCHOICETYPE(pa_spake_choice, union krb5_spake_message_choices,
+ enum krb5_spake_msgtype, pa_spake_alternatives);
+DEFCOUNTEDTYPE_SIGNED(pa_spake, krb5_pa_spake, u, choice, pa_spake_choice);
+MAKE_ENCODER(encode_krb5_pa_spake, pa_spake);
+MAKE_DECODER(decode_krb5_pa_spake, pa_spake);
diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c
index a631807d3..e1ea1494a 100644
--- a/src/lib/krb5/krb/kfree.c
+++ b/src/lib/krb5/krb/kfree.c
@@ -51,6 +51,7 @@
*/
#include "k5-int.h"
+#include "k5-spake.h"
#include <assert.h>
void KRB5_CALLCONV
@@ -890,3 +891,42 @@ k5_free_secure_cookie(krb5_context context, krb5_secure_cookie *val)
k5_zapfree_pa_data(val->data);
free(val);
}
+
+void
+k5_free_spake_factor(krb5_context context, krb5_spake_factor *val)
+{
+ if (val == NULL)
+ return;
+ krb5_free_data(context, val->data);
+ free(val);
+}
+
+void
+k5_free_pa_spake(krb5_context context, krb5_pa_spake *val)
+{
+ krb5_spake_factor **f;
+
+ if (val == NULL)
+ return;
+ switch (val->choice) {
+ case SPAKE_MSGTYPE_SUPPORT:
+ free(val->u.support.groups);
+ break;
+ case SPAKE_MSGTYPE_CHALLENGE:
+ krb5_free_data_contents(context, &val->u.challenge.pubkey);
+ for (f = val->u.challenge.factors; f != NULL && *f != NULL; f++)
+ k5_free_spake_factor(context, *f);
+ free(val->u.challenge.factors);
+ break;
+ case SPAKE_MSGTYPE_RESPONSE:
+ krb5_free_data_contents(context, &val->u.response.pubkey);
+ krb5_free_data_contents(context, &val->u.response.factor.ciphertext);
+ break;
+ case SPAKE_MSGTYPE_ENCDATA:
+ krb5_free_data_contents(context, &val->u.encdata.ciphertext);
+ break;
+ default:
+ break;
+ }
+ free(val);
+}
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index ed6cad6ad..622bc3673 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -36,6 +36,7 @@ decode_krb5_pa_otp_req
decode_krb5_pa_otp_enc_req
decode_krb5_pa_pac_req
decode_krb5_pa_s4u_x509_user
+decode_krb5_pa_spake
decode_krb5_padata_sequence
decode_krb5_priv
decode_krb5_safe
@@ -44,6 +45,7 @@ decode_krb5_sam_challenge_2_body
decode_krb5_sam_response_2
decode_krb5_secure_cookie
decode_krb5_setpw_req
+decode_krb5_spake_factor
decode_krb5_tgs_rep
decode_krb5_tgs_req
decode_krb5_ticket
@@ -85,6 +87,7 @@ encode_krb5_pa_otp_challenge
encode_krb5_pa_otp_req
encode_krb5_pa_otp_enc_req
encode_krb5_pa_s4u_x509_user
+encode_krb5_pa_spake
encode_krb5_padata_sequence
encode_krb5_pkinit_supp_pub_info
encode_krb5_priv
@@ -95,6 +98,7 @@ encode_krb5_sam_challenge_2_body
encode_krb5_sam_response_2
encode_krb5_secure_cookie
encode_krb5_sp80056a_other_info
+encode_krb5_spake_factor
encode_krb5_tgs_rep
encode_krb5_tgs_req
encode_krb5_ticket
@@ -128,7 +132,9 @@ k5_free_kkdcp_message
k5_free_pa_otp_challenge
k5_free_pa_otp_req
k5_free_secure_cookie
+k5_free_pa_spake
k5_free_serverlist
+k5_free_spake_factor
k5_hostrealm_free_context
k5_init_trace
k5_is_string_numeric
diff --git a/src/tests/asn.1/Makefile.in b/src/tests/asn.1/Makefile.in
index fec4e109e..ec9c67495 100644
--- a/src/tests/asn.1/Makefile.in
+++ b/src/tests/asn.1/Makefile.in
@@ -9,7 +9,7 @@ SRCS= $(srcdir)/krb5_encode_test.c $(srcdir)/krb5_decode_test.c \
ASN1SRCS= $(srcdir)/krb5.asn1 $(srcdir)/pkix.asn1 $(srcdir)/otp.asn1 \
$(srcdir)/pkinit.asn1 $(srcdir)/pkinit-agility.asn1 \
- $(srcdir)/cammac.asn1
+ $(srcdir)/cammac.asn1 $(srcdir)/spake.asn1
all: krb5_encode_test krb5_decode_test krb5_decode_leak t_trval
diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c
index f17f9b1f1..ee70fa4b9 100644
--- a/src/tests/asn.1/krb5_decode_test.c
+++ b/src/tests/asn.1/krb5_decode_test.c
@@ -25,6 +25,7 @@
*/
#include "k5-int.h"
+#include "k5-spake.h"
#include "ktest.h"
#include "com_err.h"
#include "utility.h"
@@ -1107,6 +1108,42 @@ int main(argc, argv)
ktest_empty_secure_cookie(&ref);
}
+ /****************************************************************/
+ /* decode_krb5_spake_factor */
+ {
+ setup(krb5_spake_factor,ktest_make_minimal_spake_factor);
+ decode_run("spake_factor","(optionals NULL)","30 05 A0 03 02 01 01",decode_krb5_spake_factor,ktest_equal_spake_factor,k5_free_spake_factor);
+ ktest_empty_spake_factor(&ref);
+ }
+ {
+ setup(krb5_spake_factor,ktest_make_maximal_spake_factor);
+ decode_run("spake_factor","","30 0E A0 03 02 01 02 A1 07 04 05 66 64 61 74 61",decode_krb5_spake_factor,ktest_equal_spake_factor,k5_free_spake_factor);
+ ktest_empty_spake_factor(&ref);
+ }
+
+ /****************************************************************/
+ /* decode_krb5_pa_spake */
+ {
+ setup(krb5_pa_spake,ktest_make_support_pa_spake);
+ decode_run("pa_spake","(support)","A0 0C 30 0A A0 08 30 06 02 01 01 02 01 02",decode_krb5_pa_spake,ktest_equal_pa_spake,k5_free_pa_spake);
+ ktest_empty_pa_spake(&ref);
+ }
+ {
+ setup(krb5_pa_spake,ktest_make_challenge_pa_spake);
+ decode_run("pa_spake","(challenge)","A1 2D 30 2B A0 03 02 01 01 A1 09 04 07 54 20 76 61 6C 75 65 A2 19 30 17 30 05 A0 03 02 01 01 30 0E A0 03 02 01 02 A1 07 04 05 66 64 61 74 61",decode_krb5_pa_spake,ktest_equal_pa_spake,k5_free_pa_spake);
+ ktest_empty_pa_spake(&ref);
+ }
+ {
+ setup(krb5_pa_spake,ktest_make_response_pa_spake);
+ decode_run("pa_spake","(response)","A2 34 30 32 A0 09 04 07 53 20 76 61 6C 75 65 A1 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65",decode_krb5_pa_spake,ktest_equal_pa_spake,k5_free_pa_spake);
+ ktest_empty_pa_spake(&ref);
+ }
+ {
+ setup(krb5_pa_spake,ktest_make_encdata_pa_spake);
+ decode_run("pa_spake","(encdata)","A3 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65",decode_krb5_pa_spake,ktest_equal_pa_spake,k5_free_pa_spake);
+ ktest_empty_pa_spake(&ref);
+ }
+
#ifndef DISABLE_PKINIT
/****************************************************************/
diff --git a/src/tests/asn.1/krb5_encode_test.c b/src/tests/asn.1/krb5_encode_test.c
index f5710b68c..3efbfb4c0 100644
--- a/src/tests/asn.1/krb5_encode_test.c
+++ b/src/tests/asn.1/krb5_encode_test.c
@@ -759,6 +759,35 @@ main(argc, argv)
encode_run(cookie, "secure_cookie", "", encode_krb5_secure_cookie);
ktest_empty_secure_cookie(&cookie);
}
+ /****************************************************************/
+ /* encode_krb5_spake_factor */
+ {
+ krb5_spake_factor factor;
+ ktest_make_minimal_spake_factor(&factor);
+ encode_run(factor, "spake_factor", "(optionals NULL)",
+ encode_krb5_spake_factor);
+ ktest_empty_spake_factor(&factor);
+ ktest_make_maximal_spake_factor(&factor);
+ encode_run(factor, "spake_factor", "", encode_krb5_spake_factor);
+ ktest_empty_spake_factor(&factor);
+ }
+ /****************************************************************/
+ /* encode_krb5_pa_spake */
+ {
+ krb5_pa_spake pa_spake;
+ ktest_make_support_pa_spake(&pa_spake);
+ encode_run(pa_spake, "pa_spake", "(support)", encode_krb5_pa_spake);
+ ktest_empty_pa_spake(&pa_spake);
+ ktest_make_challenge_pa_spake(&pa_spake);
+ encode_run(pa_spake, "pa_spake", "(challenge)", encode_krb5_pa_spake);
+ ktest_empty_pa_spake(&pa_spake);
+ ktest_make_response_pa_spake(&pa_spake);
+ encode_run(pa_spake, "pa_spake", "(response)", encode_krb5_pa_spake);
+ ktest_empty_pa_spake(&pa_spake);
+ ktest_make_encdata_pa_spake(&pa_spake);
+ encode_run(pa_spake, "pa_spake", "(encdata)", encode_krb5_pa_spake);
+ ktest_empty_pa_spake(&pa_spake);
+ }
#ifndef DISABLE_PKINIT
/****************************************************************/
/* encode_krb5_pa_pk_as_req */
diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c
index cf63f3f66..5bfdc5be2 100644
--- a/src/tests/asn.1/ktest.c
+++ b/src/tests/asn.1/ktest.c
@@ -1018,6 +1018,66 @@ ktest_make_sample_secure_cookie(krb5_secure_cookie *p)
p->time = SAMPLE_TIME;
}
+void
+ktest_make_minimal_spake_factor(krb5_spake_factor *p)
+{
+ p->type = 1;
+ p->data = NULL;
+}
+
+void
+ktest_make_maximal_spake_factor(krb5_spake_factor *p)
+{
+ p->type = 2;
+ p->data = ealloc(sizeof(*p->data));
+ krb5_data_parse(p->data, "fdata");
+}
+
+void
+ktest_make_support_pa_spake(krb5_pa_spake *p)
+{
+ krb5_spake_support *s = &p->u.support;
+
+ s->ngroups = 2;
+ s->groups = ealloc(s->ngroups * sizeof(*s->groups));
+ s->groups[0] = 1;
+ s->groups[1] = 2;
+ p->choice = SPAKE_MSGTYPE_SUPPORT;
+}
+
+void
+ktest_make_challenge_pa_spake(krb5_pa_spake *p)
+{
+ krb5_spake_challenge *c = &p->u.challenge;
+
+ c->group = 1;
+ krb5_data_parse(&c->pubkey, "T value");
+ c->factors = ealloc(3 * sizeof(*c->factors));
+ c->factors[0] = ealloc(sizeof(*c->factors[0]));
+ ktest_make_minimal_spake_factor(c->factors[0]);
+ c->factors[1] = ealloc(sizeof(*c->factors[1]));
+ ktest_make_maximal_spake_factor(c->factors[1]);
+ c->factors[2] = NULL;
+ p->choice = SPAKE_MSGTYPE_CHALLENGE;
+}
+
+void
+ktest_make_response_pa_spake(krb5_pa_spake *p)
+{
+ krb5_spake_response *r = &p->u.response;
+
+ krb5_data_parse(&r->pubkey, "S value");
+ ktest_make_sample_enc_data(&r->factor);
+ p->choice = SPAKE_MSGTYPE_RESPONSE;
+}
+
+void
+ktest_make_encdata_pa_spake(krb5_pa_spake *p)
+{
+ ktest_make_sample_enc_data(&p->u.encdata);
+ p->choice = SPAKE_MSGTYPE_ENCDATA;
+}
+
/****************************************************************/
/* destructors */
@@ -1858,3 +1918,40 @@ ktest_empty_secure_cookie(krb5_secure_cookie *p)
{
ktest_empty_pa_data_array(p->data);
}
+
+void
+ktest_empty_spake_factor(krb5_spake_factor *p)
+{
+ krb5_free_data(NULL, p->data);
+ p->data = NULL;
+}
+
+void
+ktest_empty_pa_spake(krb5_pa_spake *p)
+{
+ krb5_spake_factor **f;
+
+ switch (p->choice) {
+ case SPAKE_MSGTYPE_SUPPORT:
+ free(p->u.support.groups);
+ break;
+ case SPAKE_MSGTYPE_CHALLENGE:
+ ktest_empty_data(&p->u.challenge.pubkey);
+ for (f = p->u.challenge.factors; *f != NULL; f++) {
+ ktest_empty_spake_factor(*f);
+ free(*f);
+ }
+ free(p->u.challenge.factors);
+ break;
+ case SPAKE_MSGTYPE_RESPONSE:
+ ktest_empty_data(&p->u.response.pubkey);
+ ktest_destroy_enc_data(&p->u.response.factor);
+ break;
+ case SPAKE_MSGTYPE_ENCDATA:
+ ktest_destroy_enc_data(&p->u.encdata);
+ break;
+ default:
+ break;
+ }
+ p->choice = SPAKE_MSGTYPE_UNKNOWN;
+}
diff --git a/src/tests/asn.1/ktest.h b/src/tests/asn.1/ktest.h
index 493303cc8..1413cfae1 100644
--- a/src/tests/asn.1/ktest.h
+++ b/src/tests/asn.1/ktest.h
@@ -28,6 +28,7 @@
#define __KTEST_H__
#include "k5-int.h"
+#include "k5-spake.h"
#include "kdb.h"
#define SAMPLE_USEC 123456
@@ -124,6 +125,12 @@ void ktest_make_sample_kkdcp_message(krb5_kkdcp_message *p);
void ktest_make_minimal_cammac(krb5_cammac *p);
void ktest_make_maximal_cammac(krb5_cammac *p);
void ktest_make_sample_secure_cookie(krb5_secure_cookie *p);
+void ktest_make_minimal_spake_factor(krb5_spake_factor *p);
+void ktest_make_maximal_spake_factor(krb5_spake_factor *p);
+void ktest_make_support_pa_spake(krb5_pa_spake *p);
+void ktest_make_challenge_pa_spake(krb5_pa_spake *p);
+void ktest_make_response_pa_spake(krb5_pa_spake *p);
+void ktest_make_encdata_pa_spake(krb5_pa_spake *p);
/*----------------------------------------------------------------------*/
@@ -209,6 +216,8 @@ void ktest_empty_ldap_seqof_key_data(krb5_context, ldap_seqof_key_data *p);
void ktest_empty_kkdcp_message(krb5_kkdcp_message *p);
void ktest_empty_cammac(krb5_cammac *p);
void ktest_empty_secure_cookie(krb5_secure_cookie *p);
+void ktest_empty_spake_factor(krb5_spake_factor *p);
+void ktest_empty_pa_spake(krb5_pa_spake *p);
extern krb5_context test_context;
extern char *sample_principal_name;
diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c
index e8bb88944..714cc4398 100644
--- a/src/tests/asn.1/ktest_equal.c
+++ b/src/tests/asn.1/ktest_equal.c
@@ -853,6 +853,13 @@ ktest_equal_sequence_of_otp_tokeninfo(krb5_otp_tokeninfo **ref,
array_compare(ktest_equal_otp_tokeninfo);
}
+int
+ktest_equal_sequence_of_spake_factor(krb5_spake_factor **ref,
+ krb5_spake_factor **var)
+{
+ array_compare(ktest_equal_spake_factor);
+}
+
#ifndef DISABLE_PKINIT
static int
@@ -1094,3 +1101,45 @@ ktest_equal_secure_cookie(krb5_secure_cookie *ref, krb5_secure_cookie *var)
p = p && ref->time == ref->time;
return p;
}
+
+int
+ktest_equal_spake_factor(krb5_spake_factor *ref, krb5_spake_factor *var)
+{
+ int p = TRUE;
+ if (ref == var) return TRUE;
+ else if (ref == NULL || var == NULL) return FALSE;
+ p = p && scalar_equal(type);
+ p = p && ptr_equal(data,ktest_equal_data);
+ return p;
+}
+
+int
+ktest_equal_pa_spake(krb5_pa_spake *ref, krb5_pa_spake *var)
+{
+ int p = TRUE;
+ if (ref == var) return TRUE;
+ else if (ref == NULL || var == NULL) return FALSE;
+ else if (ref->choice != var->choice) return FALSE;
+ switch (ref->choice) {
+ case SPAKE_MSGTYPE_SUPPORT:
+ p = p && scalar_equal(u.support.ngroups);
+ p = p && (memcmp(ref->u.support.groups,var->u.support.groups,
+ ref->u.support.ngroups * sizeof(int32_t)) == 0);
+ break;
+ case SPAKE_MSGTYPE_CHALLENGE:
+ p = p && struct_equal(u.challenge.pubkey,ktest_equal_data);
+ p = p && ptr_equal(u.challenge.factors,
+ ktest_equal_sequence_of_spake_factor);
+ break;
+ case SPAKE_MSGTYPE_RESPONSE:
+ p = p && struct_equal(u.response.pubkey,ktest_equal_data);
+ p = p && struct_equal(u.response.factor,ktest_equal_enc_data);
+ break;
+ case SPAKE_MSGTYPE_ENCDATA:
+ p = p && struct_equal(u.encdata,ktest_equal_enc_data);
+ break;
+ default:
+ break;
+ }
+ return p;
+}
diff --git a/src/tests/asn.1/ktest_equal.h b/src/tests/asn.1/ktest_equal.h
index c7b5d7467..cfa82ac6e 100644
--- a/src/tests/asn.1/ktest_equal.h
+++ b/src/tests/asn.1/ktest_equal.h
@@ -28,6 +28,7 @@
#define __KTEST_EQUAL_H__
#include "k5-int.h"
+#include "k5-spake.h"
#include "kdb.h"
/* int ktest_equal_structure(krb5_structure *ref, *var) */
@@ -97,6 +98,8 @@ ktest_equal_sequence_of_algorithm_identifier(krb5_algorithm_identifier **ref,
krb5_algorithm_identifier **var);
int ktest_equal_sequence_of_otp_tokeninfo(krb5_otp_tokeninfo **ref,
krb5_otp_tokeninfo **var);
+int ktest_equal_sequence_of_spake_factor(krb5_spake_factor **ref,
+ krb5_spake_factor **var);
len_array(ktest_equal_array_of_enctype,krb5_enctype);
len_array(ktest_equal_array_of_data,krb5_data);
@@ -152,4 +155,7 @@ int ktest_equal_cammac(krb5_cammac *ref, krb5_cammac *var);
int ktest_equal_secure_cookie(krb5_secure_cookie *ref,
krb5_secure_cookie *var);
+generic(ktest_equal_spake_factor, krb5_spake_factor);
+generic(ktest_equal_pa_spake, krb5_pa_spake);
+
#endif
diff --git a/src/tests/asn.1/make-vectors.c b/src/tests/asn.1/make-vectors.c
index 3cb8a45ba..2fc85466b 100644
--- a/src/tests/asn.1/make-vectors.c
+++ b/src/tests/asn.1/make-vectors.c
@@ -40,6 +40,8 @@
#include <PA-OTP-REQUEST.h>
#include <PA-OTP-ENC-REQUEST.h>
#include <AD-CAMMAC.h>
+#include <SPAKESecondFactor.h>
+#include <PA-SPAKE.h>
static unsigned char buf[8192];
static size_t buf_pos;
@@ -168,6 +170,36 @@ static struct other_verifiers overfs = { { verifiers, 2, 2 } };
static AD_CAMMAC_t cammac_2 = { { { (void *)adlist_2, 2, 2 } },
&vmac_1, &vmac_2, &overfs };
+/* SPAKESecondFactor */
+static SPAKESecondFactor_t factor_1 = { 1, NULL };
+static OCTET_STRING_t factor_data = { "fdata", 5 };
+static SPAKESecondFactor_t factor_2 = { 2, &factor_data };
+
+/* PA-SPAKE (support) */
+static Int32_t group_1 = 1, group_2 = 2, *groups[] = { &group_1, &group_2 };
+static PA_SPAKE_t pa_spake_1 = { PA_SPAKE_PR_support,
+ { .support = { { groups, 2, 2 } } } };
+
+/* PA-SPAKE (challenge) */
+static SPAKESecondFactor_t *factors[2] = { &factor_1, &factor_2 };
+static PA_SPAKE_t pa_spake_2 = { PA_SPAKE_PR_challenge,
+ { .challenge = { 1, { "T value", 7 },
+ { factors, 2, 2 } } } };
+
+/* PA-SPAKE (response) */
+UInt32_t enctype_5 = 5;
+static PA_SPAKE_t pa_spake_3 = { PA_SPAKE_PR_response,
+ { .response = { { "S value", 7 },
+ { 0, &enctype_5,
+ { "krbASN.1 test message",
+ 21 } } } } };
+
+/* PA-SPAKE (encdata) */
+static PA_SPAKE_t pa_spake_4 = { PA_SPAKE_PR_encdata,
+ { .encdata = { 0, &enctype_5,
+ { "krbASN.1 test message",
+ 21 } } } };
+
static int
consume(const void *data, size_t size, void *dummy)
{
@@ -272,6 +304,30 @@ main()
der_encode(&asn_DEF_AD_CAMMAC, &cammac_2, consume, NULL);
printbuf();
+ printf("\nMinimal SPAKESecondFactor:\n");
+ der_encode(&asn_DEF_SPAKESecondFactor, &factor_1, consume, NULL);
+ printbuf();
+
+ printf("\nMaximal SPAKESecondFactor:\n");
+ der_encode(&asn_DEF_SPAKESecondFactor, &factor_2, consume, NULL);
+ printbuf();
+
+ printf("\nPA-SPAKE (support):\n");
+ der_encode(&asn_DEF_PA_SPAKE, &pa_spake_1, consume, NULL);
+ printbuf();
+
+ printf("\nPA-SPAKE (challenge):\n");
+ der_encode(&asn_DEF_PA_SPAKE, &pa_spake_2, consume, NULL);
+ printbuf();
+
+ printf("\nPA-SPAKE (response):\n");
+ der_encode(&asn_DEF_PA_SPAKE, &pa_spake_3, consume, NULL);
+ printbuf();
+
+ printf("\nPA-SPAKE (encdata):\n");
+ der_encode(&asn_DEF_PA_SPAKE, &pa_spake_4, consume, NULL);
+ printbuf();
+
printf("\n");
return 0;
}
diff --git a/src/tests/asn.1/reference_encode.out b/src/tests/asn.1/reference_encode.out
index 824e0798b..a76deead2 100644
--- a/src/tests/asn.1/reference_encode.out
+++ b/src/tests/asn.1/reference_encode.out
@@ -72,3 +72,9 @@ encode_krb5_kkdcp_message: 30 82 01 FC A0 82 01 EC 04 82 01 E8 6A 82 01 E4 30 82
encode_krb5_cammac(optionals NULL): 30 12 A0 10 30 0E 30 0C A0 03 02 01 01 A1 05 04 03 61 64 31
encode_krb5_cammac: 30 81 F2 A0 1E 30 1C 30 0C A0 03 02 01 01 A1 05 04 03 61 64 31 30 0C A0 03 02 01 02 A1 05 04 03 61 64 32 A1 3D 30 3B A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 03 02 01 05 A2 03 02 01 10 A3 13 30 11 A0 03 02 01 01 A1 0A 04 08 63 6B 73 75 6D 6B 64 63 A2 3D 30 3B A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 03 02 01 05 A2 03 02 01 10 A3 13 30 11 A0 03 02 01 01 A1 0A 04 08 63 6B 73 75 6D 73 76 63 A3 52 30 50 30 13 A3 11 30 0F A0 03 02 01 01 A1 08 04 06 63 6B 73 75 6D 31 30 39 A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 03 02 01 05 A2 03 02 01 10 A3 11 30 0F A0 03 02 01 01 A1 08 04 06 63 6B 73 75 6D 32
encode_krb5_secure_cookie: 30 2C 02 04 2D F8 02 25 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61
+encode_krb5_spake_factor(optionals NULL): 30 05 A0 03 02 01 01
+encode_krb5_spake_factor: 30 0E A0 03 02 01 02 A1 07 04 05 66 64 61 74 61
+encode_krb5_pa_spake(support): A0 0C 30 0A A0 08 30 06 02 01 01 02 01 02
+encode_krb5_pa_spake(challenge): A1 2D 30 2B A0 03 02 01 01 A1 09 04 07 54 20 76 61 6C 75 65 A2 19 30 17 30 05 A0 03 02 01 01 30 0E A0 03 02 01 02 A1 07 04 05 66 64 61 74 61
+encode_krb5_pa_spake(response): A2 34 30 32 A0 09 04 07 53 20 76 61 6C 75 65 A1 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65
+encode_krb5_pa_spake(encdata): A3 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65
diff --git a/src/tests/asn.1/spake.asn1 b/src/tests/asn.1/spake.asn1
new file mode 100644
index 000000000..50718d8ad
--- /dev/null
+++ b/src/tests/asn.1/spake.asn1
@@ -0,0 +1,44 @@
+KerberosV5SPAKE {
+ iso(1) identified-organization(3) dod(6) internet(1)
+ security(5) kerberosV5(2) modules(4) spake(8)
+} DEFINITIONS EXPLICIT TAGS ::= BEGIN
+
+IMPORTS
+ EncryptedData, Int32
+ FROM KerberosV5Spec2 { iso(1) identified-organization(3)
+ dod(6) internet(1) security(5) kerberosV5(2) modules(4)
+ krb5spec2(2) };
+ -- as defined in RFC 4120.
+
+SPAKESupport ::= SEQUENCE {
+ groups [0] SEQUENCE (SIZE(1..MAX)) OF Int32,
+ ...
+}
+
+SPAKEChallenge ::= SEQUENCE {
+ group [0] Int32,
+ pubkey [1] OCTET STRING,
+ factors [2] SEQUENCE (SIZE(1..MAX)) OF SPAKESecondFactor,
+ ...
+}
+
+SPAKESecondFactor ::= SEQUENCE {
+ type [0] Int32,
+ data [1] OCTET STRING OPTIONAL
+}
+
+SPAKEResponse ::= SEQUENCE {
+ pubkey [0] OCTET STRING,
+ factor [1] EncryptedData, -- SPAKESecondFactor
+ ...
+}
+
+PA-SPAKE ::= CHOICE {
+ support [0] SPAKESupport,
+ challenge [1] SPAKEChallenge,
+ response [2] SPAKEResponse,
+ encdata [3] EncryptedData,
+ ...
+}
+
+END
diff --git a/src/tests/asn.1/trval_reference.out b/src/tests/asn.1/trval_reference.out
index c27a0425b..e5c715924 100644
--- a/src/tests/asn.1/trval_reference.out
+++ b/src/tests/asn.1/trval_reference.out
@@ -1584,3 +1584,53 @@ encode_krb5_secure_cookie:
. . [Sequence/Sequence Of]
. . . [1] [Integer] 13
. . . [2] [Octet String] "pa-data"
+
+encode_krb5_spake_factor(optionals NULL):
+
+[Sequence/Sequence Of]
+. [0] [Integer] 1
+
+encode_krb5_spake_factor:
+
+[Sequence/Sequence Of]
+. [0] [Integer] 2
+. [1] [Octet String] "fdata"
+
+encode_krb5_pa_spake(support):
+
+[CONT 0]
+. [Sequence/Sequence Of]
+. . [0] [Sequence/Sequence Of]
+. . . [Integer] 1
+. . . [Integer] 2
+
+encode_krb5_pa_spake(challenge):
+
+[CONT 1]
+. [Sequence/Sequence Of]
+. . [0] [Integer] 1
+. . [1] [Octet String] "T value"
+. . [2] [Sequence/Sequence Of]
+. . . [Sequence/Sequence Of]
+. . . . [0] [Integer] 1
+. . . [Sequence/Sequence Of]
+. . . . [0] [Integer] 2
+. . . . [1] [Octet String] "fdata"
+
+encode_krb5_pa_spake(response):
+
+[CONT 2]
+. [Sequence/Sequence Of]
+. . [0] [Octet String] "S value"
+. . [1] [Sequence/Sequence Of]
+. . . [0] [Integer] 0
+. . . [1] [Integer] 5
+. . . [2] [Octet String] "krbASN.1 test message"
+
+encode_krb5_pa_spake(encdata):
+
+[CONT 3]
+. [Sequence/Sequence Of]
+. . [0] [Integer] 0
+. . [1] [Integer] 5
+. . [2] [Octet String] "krbASN.1 test message"

View File

@ -0,0 +1,360 @@
From d4a512e571a93318d37cbf7d18a120f317b87e97 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Thu, 11 Feb 2021 15:33:10 +0100
Subject: [PATCH] Add KCM_OP_GET_CRED_LIST for faster iteration
For large caches, one IPC operation per credential dominates the cost
of iteration. Instead transfer the whole list of credentials to the
client in one IPC operation.
Add optional support for the new opcode to the test KCM server to
allow testing of the main and fallback code paths.
[ghudson@mit.edu: fixed memory leaks and potential memory errors;
adjusted code style and comments; rewrote commit message; added
kcmserver.py support and tests]
ticket: 8990 (new)
(cherry picked from commit 81bdb47d8ded390263d8ee48f71d5c312b4f1736)
(cherry picked from commit a0ee8b02e56c65e5dcd569caed0e151cef004ef4)
(cherry picked from commit baf60dbdeceb3cad35cad7d9930782f94b6c8221)
---
src/include/kcm.h | 12 ++-
src/lib/krb5/ccache/cc_kcm.c | 144 ++++++++++++++++++++++++++++++++---
src/tests/kcmserver.py | 28 ++++++-
src/tests/t_ccache.py | 10 ++-
4 files changed, 175 insertions(+), 19 deletions(-)
diff --git a/src/include/kcm.h b/src/include/kcm.h
index 5ea1447cd..e4140c3a0 100644
--- a/src/include/kcm.h
+++ b/src/include/kcm.h
@@ -51,9 +51,9 @@
*
* All replies begin with a 32-bit big-endian reply code.
*
- * Parameters are appended to the request or reply with no delimiters. Flags
- * and time offsets are stored as 32-bit big-endian integers. Names are
- * marshalled as zero-terminated strings. Principals and credentials are
+ * Parameters are appended to the request or reply with no delimiters. Flags,
+ * time offsets, and lengths are stored as 32-bit big-endian integers. Names
+ * are marshalled as zero-terminated strings. Principals and credentials are
* marshalled in the v4 FILE ccache format. UUIDs are 16 bytes. UUID lists
* are not delimited, so nothing can come after them.
*/
@@ -89,7 +89,11 @@ typedef enum kcm_opcode {
KCM_OP_HAVE_NTLM_CRED,
KCM_OP_DEL_NTLM_CRED,
KCM_OP_DO_NTLM_AUTH,
- KCM_OP_GET_NTLM_USER_LIST
+ KCM_OP_GET_NTLM_USER_LIST,
+
+ /* MIT extensions */
+ KCM_OP_MIT_EXTENSION_BASE = 13000,
+ KCM_OP_GET_CRED_LIST, /* (name) -> (count, count*{len, cred}) */
} kcm_opcode;
#endif /* KCM_H */
diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c
index a76a285d9..197a10fba 100644
--- a/src/lib/krb5/ccache/cc_kcm.c
+++ b/src/lib/krb5/ccache/cc_kcm.c
@@ -61,6 +61,17 @@ struct uuid_list {
size_t pos;
};
+struct cred_list {
+ krb5_creds *creds;
+ size_t count;
+ size_t pos;
+};
+
+struct kcm_cursor {
+ struct uuid_list *uuids;
+ struct cred_list *creds;
+};
+
struct kcmio {
SOCKET fd;
#ifdef __APPLE__
@@ -489,6 +500,69 @@ free_uuid_list(struct uuid_list *uuids)
free(uuids);
}
+static void
+free_cred_list(struct cred_list *list)
+{
+ size_t i;
+
+ if (list == NULL)
+ return;
+
+ /* Creds are transferred to the caller as list->pos is incremented, so we
+ * can start freeing there. */
+ for (i = list->pos; i < list->count; i++)
+ krb5_free_cred_contents(NULL, &list->creds[i]);
+ free(list->creds);
+ free(list);
+}
+
+/* Fetch a cred list from req->reply. */
+static krb5_error_code
+kcmreq_get_cred_list(struct kcmreq *req, struct cred_list **creds_out)
+{
+ struct cred_list *list;
+ const unsigned char *data;
+ krb5_error_code ret = 0;
+ size_t count, len, i;
+
+ *creds_out = NULL;
+
+ /* Check a rough bound on the count to prevent very large allocations. */
+ count = k5_input_get_uint32_be(&req->reply);
+ if (count > req->reply.len / 4)
+ return KRB5_KCM_MALFORMED_REPLY;
+
+ list = malloc(sizeof(*list));
+ if (list == NULL)
+ return ENOMEM;
+
+ list->creds = NULL;
+ list->count = count;
+ list->pos = 0;
+ list->creds = k5calloc(count, sizeof(*list->creds), &ret);
+ if (list->creds == NULL) {
+ free(list);
+ return ret;
+ }
+
+ for (i = 0; i < count; i++) {
+ len = k5_input_get_uint32_be(&req->reply);
+ data = k5_input_get_bytes(&req->reply, len);
+ if (data == NULL)
+ break;
+ ret = k5_unmarshal_cred(data, len, 4, &list->creds[i]);
+ if (ret)
+ break;
+ }
+ if (i < count) {
+ free_cred_list(list);
+ return (ret == ENOMEM) ? ENOMEM : KRB5_KCM_MALFORMED_REPLY;
+ }
+
+ *creds_out = list;
+ return 0;
+}
+
static void
kcmreq_free(struct kcmreq *req)
{
@@ -753,33 +827,53 @@ kcm_start_seq_get(krb5_context context, krb5_ccache cache,
{
krb5_error_code ret;
struct kcmreq req = EMPTY_KCMREQ;
- struct uuid_list *uuids;
+ struct uuid_list *uuids = NULL;
+ struct cred_list *creds = NULL;
+ struct kcm_cursor *cursor;
*cursor_out = NULL;
get_kdc_offset(context, cache);
- kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache);
+ kcmreq_init(&req, KCM_OP_GET_CRED_LIST, cache);
ret = cache_call(context, cache, &req);
- if (ret)
+ if (ret == 0) {
+ /* GET_CRED_LIST is available. */
+ ret = kcmreq_get_cred_list(&req, &creds);
+ if (ret)
+ goto cleanup;
+ } else if (ret == KRB5_FCC_INTERNAL) {
+ /* Fall back to GET_CRED_UUID_LIST. */
+ kcmreq_free(&req);
+ kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache);
+ ret = cache_call(context, cache, &req);
+ if (ret)
+ goto cleanup;
+ ret = kcmreq_get_uuid_list(&req, &uuids);
+ if (ret)
+ goto cleanup;
+ } else {
goto cleanup;
- ret = kcmreq_get_uuid_list(&req, &uuids);
- if (ret)
+ }
+
+ cursor = k5alloc(sizeof(*cursor), &ret);
+ if (cursor == NULL)
goto cleanup;
- *cursor_out = (krb5_cc_cursor)uuids;
+ cursor->uuids = uuids;
+ cursor->creds = creds;
+ *cursor_out = (krb5_cc_cursor)cursor;
cleanup:
kcmreq_free(&req);
return ret;
}
-static krb5_error_code KRB5_CALLCONV
-kcm_next_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor,
- krb5_creds *cred_out)
+static krb5_error_code
+next_cred_by_uuid(krb5_context context, krb5_ccache cache,
+ struct uuid_list *uuids, krb5_creds *cred_out)
{
krb5_error_code ret;
struct kcmreq req;
- struct uuid_list *uuids = (struct uuid_list *)*cursor;
memset(cred_out, 0, sizeof(*cred_out));
@@ -797,11 +891,39 @@ kcm_next_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor,
return map_invalid(ret);
}
+static krb5_error_code KRB5_CALLCONV
+kcm_next_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor,
+ krb5_creds *cred_out)
+{
+ struct kcm_cursor *c = (struct kcm_cursor *)*cursor;
+ struct cred_list *list;
+
+ if (c->uuids != NULL)
+ return next_cred_by_uuid(context, cache, c->uuids, cred_out);
+
+ list = c->creds;
+ if (list->pos >= list->count)
+ return KRB5_CC_END;
+
+ /* Transfer memory ownership of one cred to the caller. */
+ *cred_out = list->creds[list->pos];
+ memset(&list->creds[list->pos], 0, sizeof(*list->creds));
+ list->pos++;
+
+ return 0;
+}
+
static krb5_error_code KRB5_CALLCONV
kcm_end_seq_get(krb5_context context, krb5_ccache cache,
krb5_cc_cursor *cursor)
{
- free_uuid_list((struct uuid_list *)*cursor);
+ struct kcm_cursor *c = *cursor;
+
+ if (c == NULL)
+ return 0;
+ free_uuid_list(c->uuids);
+ free_cred_list(c->creds);
+ free(c);
*cursor = NULL;
return 0;
}
diff --git a/src/tests/kcmserver.py b/src/tests/kcmserver.py
index 57432e5a7..8c5e66ff1 100644
--- a/src/tests/kcmserver.py
+++ b/src/tests/kcmserver.py
@@ -23,6 +23,7 @@
# traceback.print_exception(etype, value, tb, file=f)
# sys.excepthook = ehook
+import optparse
import select
import socket
import struct
@@ -49,12 +50,14 @@ class KCMOpcodes(object):
SET_DEFAULT_CACHE = 21
GET_KDC_OFFSET = 22
SET_KDC_OFFSET = 23
+ GET_CRED_LIST = 13001
class KRB5Errors(object):
KRB5_CC_END = -1765328242
KRB5_CC_NOSUPP = -1765328137
KRB5_FCC_NOFILE = -1765328189
+ KRB5_FCC_INTERNAL = -1765328188
def make_uuid():
@@ -183,6 +186,14 @@ def op_set_kdc_offset(argbytes):
return 0, b''
+def op_get_cred_list(argbytes):
+ name, rest = unmarshal_name(argbytes)
+ cache = get_cache(name)
+ creds = [cache.creds[u] for u in cache.cred_uuids]
+ return 0, (struct.pack('>L', len(creds)) +
+ b''.join(struct.pack('>L', len(c)) + c for c in creds))
+
+
ophandlers = {
KCMOpcodes.GEN_NEW : op_gen_new,
KCMOpcodes.INITIALIZE : op_initialize,
@@ -197,7 +208,8 @@ ophandlers = {
KCMOpcodes.GET_DEFAULT_CACHE : op_get_default_cache,
KCMOpcodes.SET_DEFAULT_CACHE : op_set_default_cache,
KCMOpcodes.GET_KDC_OFFSET : op_get_kdc_offset,
- KCMOpcodes.SET_KDC_OFFSET : op_set_kdc_offset
+ KCMOpcodes.SET_KDC_OFFSET : op_set_kdc_offset,
+ KCMOpcodes.GET_CRED_LIST : op_get_cred_list
}
# Read and respond to a request from the socket s.
@@ -215,7 +227,11 @@ def service_request(s):
majver, minver, op = struct.unpack('>BBH', req[:4])
argbytes = req[4:]
- code, payload = ophandlers[op](argbytes)
+
+ if op in ophandlers:
+ code, payload = ophandlers[op](argbytes)
+ else:
+ code, payload = KRB5Errors.KRB5_FCC_INTERNAL, b''
# The KCM response is the code (4 bytes) and the response payload.
# The Heimdal IPC response is the length of the KCM response (4
@@ -226,9 +242,15 @@ def service_request(s):
s.sendall(hipc_response)
return True
+parser = optparse.OptionParser()
+parser.add_option('-c', '--credlist', action='store_true', dest='credlist',
+ default=False, help='Support KCM_OP_GET_CRED_LIST')
+(options, args) = parser.parse_args()
+if not options.credlist:
+ del ophandlers[KCMOpcodes.GET_CRED_LIST]
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-server.bind(sys.argv[1])
+server.bind(args[0])
server.listen(5)
select_input = [server,]
sys.stderr.write('starting...\n')
diff --git a/src/tests/t_ccache.py b/src/tests/t_ccache.py
index 66804afa5..90040fb7b 100755
--- a/src/tests/t_ccache.py
+++ b/src/tests/t_ccache.py
@@ -125,10 +125,18 @@ def collection_test(realm, ccname):
collection_test(realm, 'DIR:' + os.path.join(realm.testdir, 'cc'))
+
+# Test KCM without and with GET_CRED_LIST support.
kcmserver_path = os.path.join(srctop, 'tests', 'kcmserver.py')
-realm.start_server([sys.executable, kcmserver_path, kcm_socket_path],
+kcmd = realm.start_server([sys.executable, kcmserver_path, kcm_socket_path],
+ 'starting...')
+collection_test(realm, 'KCM:')
+stop_daemon(kcmd)
+os.remove(kcm_socket_path)
+realm.start_server([sys.executable, kcmserver_path, '-c', kcm_socket_path],
'starting...')
collection_test(realm, 'KCM:')
+
if test_keyring:
def cleanup_keyring(anchor, name):
out = realm.run(['keyctl', 'list', anchor])

View File

@ -0,0 +1,673 @@
From bf3e55bcd66c5d35fddadc94fd680bdd57508bce Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 22 Dec 2022 03:05:23 -0500
Subject: [PATCH] Add PAC full checksums
A paper by Tom Tervoort noted that computing the PAC privsvr checksum
over only the server checksum is vulnerable to collision attacks
(CVE-2022-37967). In response, Microsoft has added a second KDC
checksum over the full contents of the PAC. Generate and verify full
KDC checksums in PACs for service tickets. Update the t_pac.c ticket
test case to use a ticket issued by a recent version of Active
Directory (provided by Stefan Metzmacher).
ticket: 9084 (new)
---
doc/appdev/refs/macros/index.rst | 1 +
src/include/krb5/krb5.hin | 1 +
src/lib/krb5/krb/pac.c | 92 +++++++++--------
src/lib/krb5/krb/pac_sign.c | 146 +++++++++++++++-----------
src/lib/krb5/krb/t_pac.c | 171 ++++++++++++++++++-------------
src/tests/t_authdata.py | 5 +-
6 files changed, 242 insertions(+), 174 deletions(-)
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index c6ea088742..22ef2b2f42 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -247,6 +247,7 @@ Public
KRB5_PAC_SERVER_CHECKSUM.rst
KRB5_PAC_TICKET_CHECKSUM.rst
KRB5_PAC_UPN_DNS_INFO.rst
+ KRB5_PAC_FULL_CHECKSUM.rst
KRB5_PADATA_AFS3_SALT.rst
KRB5_PADATA_AP_REQ.rst
KRB5_PADATA_AS_CHECKSUM.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 8e59628bd9..12a1d441b8 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -8187,6 +8187,7 @@ krb5_verify_authdata_kdc_issued(krb5_context context,
#define KRB5_PAC_TICKET_CHECKSUM 16 /**< Ticket checksum */
#define KRB5_PAC_ATTRIBUTES_INFO 17 /**< PAC attributes */
#define KRB5_PAC_REQUESTOR 18 /**< PAC requestor SID */
+#define KRB5_PAC_FULL_CHECKSUM 19 /**< KDC full checksum */
struct krb5_pac_data;
/** PAC data structure to convey authorization information */
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 2f6ad4e1df..9c00178a28 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -500,7 +500,8 @@ zero_signature(krb5_context context, const krb5_pac pac, krb5_ui_4 type,
size_t i;
assert(type == KRB5_PAC_SERVER_CHECKSUM ||
- type == KRB5_PAC_PRIVSVR_CHECKSUM);
+ type == KRB5_PAC_PRIVSVR_CHECKSUM ||
+ type == KRB5_PAC_FULL_CHECKSUM);
assert(data->length >= pac->data.length);
for (i = 0; i < pac->pac->cBuffers; i++) {
@@ -567,17 +568,17 @@ verify_checksum(krb5_context context, const krb5_pac pac, uint32_t buffer_type,
}
static krb5_error_code
-verify_server_checksum(krb5_context context, const krb5_pac pac,
- const krb5_keyblock *server)
+verify_pac_checksums(krb5_context context, const krb5_pac pac,
+ krb5_boolean expect_full_checksum,
+ const krb5_keyblock *server, const krb5_keyblock *privsvr)
{
krb5_error_code ret;
- krb5_data copy; /* PAC with zeroed checksums */
+ krb5_data copy, server_checksum;
+ /* Make a copy of the PAC with zeroed out server and privsvr checksums. */
ret = krb5int_copy_data_contents(context, &pac->data, &copy);
if (ret)
return ret;
-
- /* Zero out both checksum buffers */
ret = zero_signature(context, pac, KRB5_PAC_SERVER_CHECKSUM, &copy);
if (ret)
goto cleanup;
@@ -585,32 +586,46 @@ verify_server_checksum(krb5_context context, const krb5_pac pac,
if (ret)
goto cleanup;
- ret = verify_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM, server,
- KRB5_KEYUSAGE_APP_DATA_CKSUM, &copy);
+ if (server != NULL) {
+ /* Verify the server checksum over the PAC copy. */
+ ret = verify_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM, server,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &copy);
+ }
-cleanup:
- free(copy.data);
- return ret;
-}
+ if (privsvr != NULL && expect_full_checksum) {
+ /* Zero the full checksum buffer in the copy and verify the full
+ * checksum over the copy with all three checksums zeroed. */
+ ret = zero_signature(context, pac, KRB5_PAC_FULL_CHECKSUM, &copy);
+ if (ret)
+ goto cleanup;
+ ret = verify_checksum(context, pac, KRB5_PAC_FULL_CHECKSUM, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &copy);
+ if (ret)
+ goto cleanup;
+ }
-static krb5_error_code
-verify_kdc_checksum(krb5_context context, const krb5_pac pac,
- const krb5_keyblock *privsvr)
-{
- krb5_error_code ret;
- krb5_data server_checksum;
+ if (privsvr != NULL) {
+ /* Verify the privsvr checksum over the server checksum. */
+ ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
+ &server_checksum);
+ if (ret)
+ return ret;
+ if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+ server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
+ server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
- &server_checksum);
- if (ret)
- return ret;
- if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
- return KRB5_BAD_MSIZE;
- server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
- server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
+ ret = verify_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &server_checksum);
+ if (ret)
+ goto cleanup;
+ }
+
+ pac->verified = TRUE;
- return verify_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, privsvr,
- KRB5_KEYUSAGE_APP_DATA_CKSUM, &server_checksum);
+cleanup:
+ free(copy.data);
+ return ret;
}
/* Per MS-PAC 2.8.3, tickets encrypted to TGS and password change principals
@@ -638,6 +653,7 @@ krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
krb5_authdata **authdata, *orig, **ifrel = NULL, **recoded_ifrel = NULL;
uint8_t z = 0;
krb5_authdata zpac = { KV5M_AUTHDATA, KRB5_AUTHDATA_WIN2K_PAC, 1, &z };
+ krb5_boolean is_service_tkt;
size_t i, j;
*pac_out = NULL;
@@ -679,7 +695,8 @@ krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
if (ret)
goto cleanup;
- if (privsvr != NULL && k5_pac_should_have_ticket_signature(server_princ)) {
+ is_service_tkt = k5_pac_should_have_ticket_signature(server_princ);
+ if (privsvr != NULL && is_service_tkt) {
/* To check the PAC ticket signatures, re-encode the ticket with the
* PAC contents replaced by a single zero. */
orig = ifrel[j];
@@ -703,8 +720,9 @@ krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
goto cleanup;
}
- ret = krb5_pac_verify_ext(context, pac, enc_tkt->times.authtime, NULL,
- server, privsvr, FALSE);
+ ret = verify_pac_checksums(context, pac, is_service_tkt, server, privsvr);
+ if (ret)
+ goto cleanup;
*pac_out = pac;
pac = NULL;
@@ -740,14 +758,8 @@ krb5_pac_verify_ext(krb5_context context,
{
krb5_error_code ret;
- if (server != NULL) {
- ret = verify_server_checksum(context, pac, server);
- if (ret != 0)
- return ret;
- }
-
- if (privsvr != NULL) {
- ret = verify_kdc_checksum(context, pac, privsvr);
+ if (server != NULL || privsvr != NULL) {
+ ret = verify_pac_checksums(context, pac, FALSE, server, privsvr);
if (ret != 0)
return ret;
}
@@ -759,8 +771,6 @@ krb5_pac_verify_ext(krb5_context context,
return ret;
}
- pac->verified = TRUE;
-
return 0;
}
diff --git a/src/lib/krb5/krb/pac_sign.c b/src/lib/krb5/krb/pac_sign.c
index 0f9581abbb..8ea61ac17b 100644
--- a/src/lib/krb5/krb/pac_sign.c
+++ b/src/lib/krb5/krb/pac_sign.c
@@ -187,26 +187,41 @@ k5_pac_encode_header(krb5_context context, krb5_pac pac)
return 0;
}
-krb5_error_code KRB5_CALLCONV
-krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
- krb5_const_principal principal, const krb5_keyblock *server_key,
- const krb5_keyblock *privsvr_key, krb5_data *data)
+/* Find the buffer of type buftype in pac and write within it a checksum of
+ * type cksumtype over data. Set *cksum_out to the checksum. */
+static krb5_error_code
+compute_pac_checksum(krb5_context context, krb5_pac pac, uint32_t buftype,
+ const krb5_keyblock *key, krb5_cksumtype cksumtype,
+ const krb5_data *data, krb5_data *cksum_out)
{
- return krb5_pac_sign_ext(context, pac, authtime, principal, server_key,
- privsvr_key, FALSE, data);
+ krb5_error_code ret;
+ krb5_data buf;
+ krb5_crypto_iov iov[2];
+
+ ret = k5_pac_locate_buffer(context, pac, buftype, &buf);
+ if (ret)
+ return ret;
+
+ assert(buf.length > PAC_SIGNATURE_DATA_LENGTH);
+ *cksum_out = make_data(buf.data + PAC_SIGNATURE_DATA_LENGTH,
+ buf.length - PAC_SIGNATURE_DATA_LENGTH);
+ iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[0].data = *data;
+ iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
+ iov[1].data = *cksum_out;
+ return krb5_c_make_checksum_iov(context, cksumtype, key,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, iov, 2);
}
-krb5_error_code KRB5_CALLCONV
-krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
- krb5_const_principal principal,
- const krb5_keyblock *server_key,
- const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
- krb5_data *data)
+static krb5_error_code
+sign_pac(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+ krb5_const_principal principal, const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
+ krb5_boolean is_service_tkt, krb5_data *data)
{
krb5_error_code ret;
- krb5_data server_cksum, privsvr_cksum;
+ krb5_data full_cksum, server_cksum, privsvr_cksum;
krb5_cksumtype server_cksumtype, privsvr_cksumtype;
- krb5_crypto_iov iov[2];
data->length = 0;
data->data = NULL;
@@ -214,67 +229,53 @@ krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
if (principal != NULL) {
ret = k5_insert_client_info(context, pac, authtime, principal,
with_realm);
- if (ret != 0)
+ if (ret)
return ret;
}
- /* Create zeroed buffers for both checksums */
+ /* Create zeroed buffers for all checksums. */
ret = k5_insert_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM,
server_key, &server_cksumtype);
- if (ret != 0)
+ if (ret)
return ret;
-
ret = k5_insert_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
privsvr_key, &privsvr_cksumtype);
- if (ret != 0)
+ if (ret)
return ret;
+ if (is_service_tkt) {
+ ret = k5_insert_checksum(context, pac, KRB5_PAC_FULL_CHECKSUM,
+ privsvr_key, &privsvr_cksumtype);
+ if (ret)
+ return ret;
+ }
- /* Now, encode the PAC header so that the checksums will include it */
+ /* Encode the PAC header so that the checksums will include it. */
ret = k5_pac_encode_header(context, pac);
- if (ret != 0)
- return ret;
-
- /* Generate the server checksum over the entire PAC */
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
- &server_cksum);
- if (ret != 0)
+ if (ret)
return ret;
- assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
-
- iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[0].data = pac->data;
-
- iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
- iov[1].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
- iov[1].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
+ if (is_service_tkt) {
+ /* Generate a full KDC checksum over the whole PAC. */
+ ret = compute_pac_checksum(context, pac, KRB5_PAC_FULL_CHECKSUM,
+ privsvr_key, privsvr_cksumtype,
+ &pac->data, &full_cksum);
+ if (ret)
+ return ret;
+ }
- ret = krb5_c_make_checksum_iov(context, server_cksumtype,
- server_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
- iov, sizeof(iov)/sizeof(iov[0]));
- if (ret != 0)
+ /* Generate the server checksum over the whole PAC, including the full KDC
+ * checksum if we added one. */
+ ret = compute_pac_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM,
+ server_key, server_cksumtype, &pac->data,
+ &server_cksum);
+ if (ret)
return ret;
- /* Generate the privsvr checksum over the server checksum buffer */
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
+ /* Generate the privsvr checksum over the server checksum buffer. */
+ ret = compute_pac_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
+ privsvr_key, privsvr_cksumtype, &server_cksum,
&privsvr_cksum);
- if (ret != 0)
- return ret;
-
- assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
-
- iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[0].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
- iov[0].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
-
- iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
- iov[1].data.data = privsvr_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
- iov[1].data.length = privsvr_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
-
- ret = krb5_c_make_checksum_iov(context, privsvr_cksumtype,
- privsvr_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
- iov, sizeof(iov)/sizeof(iov[0]));
- if (ret != 0)
+ if (ret)
return ret;
data->data = k5memdup(pac->data.data, pac->data.length, &ret);
@@ -288,6 +289,26 @@ krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
return 0;
}
+krb5_error_code KRB5_CALLCONV
+krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+ krb5_const_principal principal, const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key, krb5_data *data)
+{
+ return sign_pac(context, pac, authtime, principal, server_key,
+ privsvr_key, FALSE, FALSE, data);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
+ krb5_data *data)
+{
+ return sign_pac(context, pac, authtime, principal, server_key, privsvr_key,
+ with_realm, FALSE, data);
+}
+
/* Add a signature over der_enc_tkt in privsvr to pac. der_enc_tkt should be
* encoded with a dummy PAC authdata element containing a single zero byte. */
static krb5_error_code
@@ -359,6 +380,7 @@ krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
krb5_error_code ret;
krb5_data *der_enc_tkt = NULL, pac_data = empty_data();
krb5_authdata **list, *pac_ad;
+ krb5_boolean is_service_tkt;
size_t count;
/* Reallocate space for another authdata element in enc_tkt. */
@@ -377,7 +399,8 @@ krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
memmove(list + 1, list, (count + 1) * sizeof(*list));
list[0] = pac_ad;
- if (k5_pac_should_have_ticket_signature(server_princ)) {
+ is_service_tkt = k5_pac_should_have_ticket_signature(server_princ);
+ if (is_service_tkt) {
ret = encode_krb5_enc_tkt_part(enc_tkt, &der_enc_tkt);
if (ret)
goto cleanup;
@@ -388,9 +411,8 @@ krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
goto cleanup;
}
- ret = krb5_pac_sign_ext(context, pac, enc_tkt->times.authtime,
- client_princ, server, privsvr, with_realm,
- &pac_data);
+ ret = sign_pac(context, pac, enc_tkt->times.authtime, client_princ, server,
+ privsvr, with_realm, is_service_tkt, &pac_data);
if (ret)
goto cleanup;
diff --git a/src/lib/krb5/krb/t_pac.c b/src/lib/krb5/krb/t_pac.c
index 173bde7bab..81f1642ab0 100644
--- a/src/lib/krb5/krb/t_pac.c
+++ b/src/lib/krb5/krb/t_pac.c
@@ -607,78 +607,102 @@ check_pac(krb5_context context, int index, const unsigned char *pdata,
static const krb5_keyblock ticket_sig_krbtgt_key = {
0, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
- 32, U("\x7a\x58\x98\xd2\xaf\xa6\xaf\xc0\x6a\xce\x06\x04\x4b\xc2\x70\x84"
- "\x9b\x8e\x0a\x6c\x4c\x07\xdc\x6f\xbb\x48\x43\xe1\xd2\xaa\x97\xf7")
+ 32, U("\x03\x73\x81\xEC\x43\x96\x7B\xC2\xAC\x3D\xF5\x2A\xAE\x95\xA6\x8E"
+ "\xBE\x24\x58\xDB\xCE\x52\x28\x20\xAF\x5E\xB7\x04\xA2\x22\x71\x4F")
};
static const krb5_keyblock ticket_sig_server_key = {
- 0, ENCTYPE_ARCFOUR_HMAC,
- 16, U("\xed\x23\x11\x20\x7a\x21\x44\x20\xbf\xc0\x8d\x36\xf7\xf6\xb2\x3e")
+ 0, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ 32, U("\x11\x4A\x84\xE3\x14\x8F\xAA\xB1\xFA\x7B\x53\x51\xB2\x8A\xC2\xF1"
+ "\xFD\x19\x6D\x61\xE0\xF3\xF2\x3E\x1F\xDB\xD3\xC1\x79\x7D\xC1\xEE")
};
+/* A ticket issued by an Active Directory KDC (Windows Server 2022), containing
+ * a PAC with a full checksum. */
static const krb5_data ticket_data = {
- .length = 972, .data =
- "\x61\x82\x03\xC8\x30\x82\x03\xC4\xA0\x03\x02\x01\x05\xA1\x0A\x1B"
- "\x08\x43\x44\x4F\x4D\x2E\x43\x4F\x4D\xA2\x0F\x30\x0D\xA0\x03\x02"
- "\x01\x01\xA1\x06\x30\x04\x1B\x02\x73\x31\xA3\x82\x03\x9E\x30\x82"
- "\x03\x9A\xA0\x03\x02\x01\x17\xA1\x03\x02\x01\x03\xA2\x82\x03\x8C"
- "\x04\x82\x03\x88\x44\x31\x61\x20\x17\xC9\xFE\xBC\xAC\x46\xB5\x77"
- "\xE9\x68\x04\x4C\x9B\x31\x91\x0C\xC1\xD4\xDD\xEF\xC7\x34\x20\x08"
- "\x90\x91\xE8\x79\xE0\xB5\x03\x26\xA4\x65\xDE\xEC\x47\x03\x2A\x8F"
- "\x61\xE7\x4D\x38\x5A\x42\x95\x5A\xF9\x2F\x41\x2C\x2A\x6E\x60\xA1"
- "\xEB\x51\xB3\xBD\x4C\x00\x41\x2A\x44\x76\x08\x37\x1A\x51\xFD\x65"
- "\x67\x7E\xBF\x3D\x90\x86\xE3\x9A\x54\x6B\x67\xA8\x08\x7A\x73\xCC"
- "\xC3\xB7\x4B\xD5\x5C\x3A\x14\x6C\xC1\x5F\x54\x4B\x92\x55\xB4\xB7"
- "\x92\x23\x3F\x53\x89\x47\x8E\x1F\x8B\xB9\xDB\x3B\x93\xE8\x70\xE4"
- "\x24\xB8\x9D\xF0\x0E\x35\x28\xF8\x7A\x27\x5D\xF7\x25\x97\x9C\xF5"
- "\x9F\x9F\x64\x04\xF2\xA3\xAB\x11\x15\xB6\xDA\x18\xD6\x46\xD5\xE6"
- "\xB8\x08\xDE\x0A\x62\xFD\xF8\xAA\x52\x90\xD9\x67\x29\xB2\xCD\x06"
- "\xB6\xB0\x50\x2B\x3F\x0F\xA3\xA5\xBF\xAA\x6E\x40\x03\xD6\x5F\x02"
- "\xBC\xD8\x18\x47\x97\x09\xD7\xE4\x96\x3B\xCB\xEB\x92\x2C\x3C\x49"
- "\xFF\x1F\x71\xE0\x52\x94\x0F\x8B\x9F\xB8\x2A\xBB\x9C\xE2\xA3\xDD"
- "\x38\x89\xE2\xB1\x0B\x9E\x1F\x7A\xB3\xE3\xD2\xB0\x94\xDC\x87\xBE"
- "\x37\xA6\xD3\xB3\x29\x35\x9A\x72\xC3\x7A\xF1\xA9\xE6\xC5\xD1\x26"
- "\x83\x65\x44\x17\xBA\x55\xA8\x5E\x94\x26\xED\xE9\x8A\x93\x11\x5D"
- "\x7E\x20\x1B\x9C\x15\x9E\x13\x37\x03\x4D\xDD\x99\x51\xD8\x66\x29"
- "\x6A\xB9\xFB\x49\xFE\x52\x78\xDA\x86\x85\xA9\xA3\xB9\xEF\xEC\xAD"
- "\x35\xA6\x8D\xAC\x0F\x75\x22\xBB\x0B\x49\x1C\x13\x52\x40\xC9\x52"
- "\x69\x09\x54\xD1\x0F\x94\x3F\x22\x48\x67\xB0\x96\x28\xAA\xE6\x28"
- "\xD9\x0C\x08\xEF\x51\xED\x15\x5E\xA2\x53\x59\xA5\x03\xB4\x06\x20"
- "\x3D\xCC\xB4\xC5\xF8\x8C\x73\x67\xA3\x21\x3D\x19\xCD\xD4\x12\x28"
- "\xD2\x93\xDE\x0D\xF0\x71\x10\x50\xD6\x33\x35\x04\x11\x64\x43\x39"
- "\xC3\xDF\x96\xE3\x66\xE3\x85\xCA\xE7\x67\x14\x3A\xF0\x43\xAA\xBB"
- "\xD4\x1D\xB5\x24\xB5\x74\x90\x25\xA7\x87\x7E\xDB\xD3\x83\x8A\x3A"
- "\x69\xA8\x2D\xAF\xB7\xB8\xF3\xDC\x13\xAF\x45\x61\x3F\x59\x39\x7E"
- "\x69\xDE\x0C\x04\xF1\x10\x6B\xB4\x56\xFA\x21\x9F\x72\x2B\x60\x86"
- "\xE3\x23\x0E\xC4\x51\xF6\xBE\xD8\xE1\x5F\xEE\x73\x4C\x17\x4C\x2C"
- "\x1B\xFB\x9F\x1F\x7A\x3B\x07\x5B\x8E\xF1\x01\xAC\xD6\x30\x94\x8A"
- "\x5D\x22\x6F\x08\xCE\xED\x5E\xB6\xDB\x86\x8C\x87\xEB\x8D\x91\xFF"
- "\x0A\x86\x30\xBD\xC0\xF8\x25\xE7\xAE\x24\x35\xF2\xFC\xE5\xFD\x1B"
- "\xB0\x05\x4A\xA3\xE5\xEB\x2E\x05\xAD\x99\x67\x49\x87\xE6\xB3\x87"
- "\x82\xA4\x59\xA7\x6E\xDD\xF2\xB6\x66\xE8\xF7\x70\xF5\xBD\xC9\x0E"
- "\xFA\x9C\x79\x84\xD4\x9B\x05\x0E\xBB\xF5\xDB\xEF\xFC\xCC\x26\xF2"
- "\x93\xCF\xD2\x04\x3C\xA9\x2C\x65\x42\x97\x86\xD8\x38\x0A\x1E\xF6"
- "\xD6\xCA\x30\xB5\x1A\xEC\xFB\xBA\x3B\x84\x57\xB0\xFD\xFB\xE6\xBC"
- "\xF2\x76\xF6\x4C\xBB\xAB\xB1\x31\xA1\x27\x7C\xE6\xE6\x81\xB6\xCE"
- "\x84\x86\x40\xB6\x40\x33\xC4\xF8\xB4\x15\xCF\xAA\xA5\x51\x78\xB9"
- "\x8B\x50\x25\xB2\x88\x86\x96\x72\x8C\x71\x4D\xB5\x3A\x94\x86\x77"
- "\x0E\x95\x9B\x16\x93\xEF\x3A\x11\x79\xBA\x83\xF7\x74\xD3\x8D\xBA"
- "\x15\xE1\x2C\x04\x57\xA8\x92\x1E\x9D\x00\x8E\x20\xFD\x30\x70\xE7"
- "\xF5\x65\x2F\x19\x0C\x94\xBA\x03\x71\x12\x96\xCD\xC8\xB4\x96\xDB"
- "\xCE\x19\xC2\xDF\x3C\xC2\xF6\x3D\x53\xED\x98\xA5\x41\x72\x2A\x22"
- "\x7B\xF3\x2B\x17\x6C\xE1\x39\x7D\xAE\x9B\x11\xF9\xC1\xA6\x9E\x9F"
- "\x89\x3C\x12\xAA\x94\x74\xA7\x4F\x70\xE8\xB9\xDE\x04\xF0\x9D\x39"
- "\x24\x2D\x92\xE8\x46\x2D\x2E\xF0\x40\x66\x1A\xD9\x27\xF9\x98\xF1"
- "\x81\x1D\x70\x62\x63\x30\x6D\xCD\x84\x04\x5F\xFA\x83\xD3\xEC\x8D"
- "\x86\xFB\x40\x61\xC1\x8A\x45\xFF\x7B\xD9\xD4\x18\x61\x7F\x51\xE3"
- "\xFC\x1E\x18\xF0\xAF\xC6\x18\x2C\xE1\x6D\x5D\xF9\x62\xFC\x20\xA3"
- "\xB2\x8A\x5F\xE5\xBB\x29\x0F\x99\x63\x07\x88\x38\x3A\x3B\x73\x2A"
- "\x6D\xDA\x3D\xA8\x0D\x8F\x56\x41\x89\x82\xE5\xB8\x61\x00\x64\x7D"
- "\x17\x0C\xCE\x03\x55\x8F\xF4\x5B\x0D\x50\xF2\xEB\x05\x67\xBE\xDB"
- "\x7B\x75\xC5\xEA\xA1\xAB\x1D\xB0\x3C\x6D\x42\x08\x0B\x9A\x45\x20"
- "\xA8\x8F\xE5\x67\x47\x30\xDE\x93\x5F\x43\x05\xEB\xA8\x2D\x80\xF5"
- "\x1A\xB8\x4A\x4E\x42\x2D\x0B\x7A\xDC\x46\x20\x2D\x13\x17\xDD\x4B"
- "\x94\x96\xAA\x1F\x06\x0C\x1F\x62\x07\x9C\x40\xA1"
+ .length = 1307, .data =
+ "\x61\x82\x05\x17\x30\x82\x05\x13\xA0\x03\x02\x01\x05\xA1\x0F\x1B"
+ "\x0D\x57\x32\x30\x32\x32\x2D\x4C\x37\x2E\x42\x41\x53\x45\xA2\x2A"
+ "\x30\x28\xA0\x03\x02\x01\x01\xA1\x21\x30\x1F\x1B\x04\x63\x69\x66"
+ "\x73\x1B\x17\x77\x32\x30\x32\x32\x2D\x31\x31\x38\x2E\x77\x32\x30"
+ "\x32\x32\x2D\x6C\x37\x2E\x62\x61\x73\x65\xA3\x82\x04\xCD\x30\x82"
+ "\x04\xC9\xA0\x03\x02\x01\x12\xA1\x03\x02\x01\x05\xA2\x82\x04\xBB"
+ "\x04\x82\x04\xB7\x44\x5C\x7B\x5A\x3F\x2E\xA3\x50\x34\xDE\xB0\x69"
+ "\x23\x2D\x47\x89\x2C\xC0\xA3\xF9\xDD\x70\xAA\xA5\x1E\xFE\x74\xE5"
+ "\x19\xA2\x4F\x65\x6C\x9E\x00\xB4\x60\x00\x7C\x0C\x29\x43\x31\x99"
+ "\x77\x02\x73\xED\xB9\x40\xF5\xD2\xD1\xC9\x20\x0F\xE3\x38\xF9\xCC"
+ "\x5E\x2A\xBD\x1F\x91\x66\x1A\xD8\x2A\x80\x3C\x2C\x00\x3C\x1E\xC9"
+ "\x2A\x29\x19\x19\x96\x18\x54\x03\x97\x8F\x1D\x5F\xDB\xE9\x66\x68"
+ "\xCD\xB1\xD5\x00\x35\x69\x49\x45\xF1\x6A\x78\x7B\x37\x71\x87\x14"
+ "\x1C\x98\x4D\x69\xCB\x1B\xD8\xF5\xA3\xD8\x53\x4A\x75\x76\x62\xBA"
+ "\x6C\x3F\xEA\x8B\x97\x21\xCA\x8A\x46\x4B\x38\xDA\x09\x9F\x5A\xC8"
+ "\x38\xFF\x34\x97\x5B\xA2\xE5\xBA\xC9\x87\x17\xD8\x08\x05\x7A\x83"
+ "\x04\xD6\x02\x8E\x9B\x18\xB6\x40\x1A\xF7\x47\x25\x24\x3E\x37\x1E"
+ "\xF6\xC1\x3A\x1F\xCA\xB3\x43\x5A\xAE\x94\x83\x31\xAF\xFB\xEE\xED"
+ "\x46\x71\xEF\xE2\x37\x37\x15\xFE\x1B\x0B\x9E\xF8\x3E\x0C\x43\x96"
+ "\xB6\x0A\x04\x78\xF8\x5E\xAA\x33\x1F\xE2\x07\x5A\x8D\xC4\x4E\x32"
+ "\x6D\xD6\xA0\xC5\xEA\x3D\x12\x59\xD4\x41\x40\x4E\xA1\xD8\xBE\xED"
+ "\x17\xCB\x68\xCC\x59\xCB\x53\xB2\x0E\x58\x8A\xA9\x33\x7F\x6F\x2B"
+ "\x37\x89\x08\x44\xBA\xC7\x67\x17\xBB\x91\xF7\xC3\x0F\x00\xF8\xAA"
+ "\xA1\x33\xA6\x08\x47\xCA\xFA\xE8\x49\x27\x45\x46\xF1\xC1\xC3\x5F"
+ "\xE2\x45\x0A\x7D\x64\x52\x8C\x2E\xE1\xDE\xFF\xB2\x64\xEC\x69\x98"
+ "\x15\xDF\x9E\xB1\xEB\xD6\x9D\x08\x06\x4E\x73\xC1\x0B\x71\x21\x05"
+ "\x9E\xBC\xA2\x17\xCF\xB3\x70\xF4\xEF\xB8\x69\xA9\x94\x27\xFD\x5E"
+ "\x72\xB1\x2D\xD2\x20\x1B\x57\x80\xAB\x38\x97\xCF\x22\x68\x4F\xB8"
+ "\xB7\x17\x53\x25\x67\x0B\xED\xD1\x58\x20\x0D\x45\xF9\x09\xFA\xE7"
+ "\x61\x3E\xDB\xC2\x59\x7B\x3A\x3B\x59\x81\x51\xAA\xA4\x81\xF4\x96"
+ "\x3B\xE1\x6F\x6F\xF4\x8E\x68\x9E\xBA\x1E\x0F\xF2\x44\x68\x11\xFC"
+ "\x2B\x5F\xBE\xF2\xEA\x07\x80\xB9\xCA\x9E\x41\xBD\x2F\x81\xF5\x11"
+ "\x2A\x12\xF3\x4F\xD6\x12\x16\x0F\x21\x90\xF1\xD3\x1E\xF1\xA4\x94"
+ "\x46\xEA\x30\xF3\x84\x06\xC1\xA4\x51\xFC\x43\x35\xBD\xEF\x4D\x89"
+ "\x1D\xA5\x44\xB2\x69\xC4\x0F\xBF\x86\x01\x08\x44\x77\xD5\xB4\xB7"
+ "\x5C\x3F\xA7\xD4\x2F\x39\x73\x85\x88\xEE\xB1\x64\x1D\x80\x6C\xEE"
+ "\x6E\x31\x90\x92\x0D\xA1\xB7\xC4\x5C\xCC\xEE\x91\xC8\xCB\x11\x2D"
+ "\x4A\x1A\x7D\x43\x8F\xEB\x60\x09\xED\x1B\x07\x58\xBE\xBC\xBD\x29"
+ "\xF3\xB3\xA3\x4F\xC5\x8A\x30\x33\xB9\xA9\x9F\x43\x08\x27\x15\xC4"
+ "\x9C\x5D\x8E\xBD\x5C\x05\xC6\x05\x9C\x87\x60\x08\x1E\xE2\x52\xB8"
+ "\x45\x8D\x28\xB6\x2C\x15\x46\x74\x9F\x0E\xAA\x6B\x70\x3A\x2A\x55"
+ "\x45\x26\xB2\x58\x4D\x35\xA6\xF1\x96\xBE\x60\xB2\x71\x7B\xF8\x54"
+ "\xB9\x90\x21\x8E\xB9\x0F\x35\x98\x5E\x88\xEB\x1A\x53\xB4\x59\x7F"
+ "\xAF\x69\x1C\x61\x67\xF4\xF6\xBD\xAC\x24\xCD\xB7\xA9\x67\xE8\xA1"
+ "\x83\x85\x5F\x11\x74\x1F\xF7\x4C\x78\x36\xEF\x50\x74\x88\x58\x4B"
+ "\x1A\x9F\x84\x9A\x9A\x05\x92\xEC\x1D\xD5\xF3\xC4\x95\x51\x28\xE2"
+ "\x3F\x32\x87\xB2\xFD\x21\x27\x66\xE4\x6B\x85\x2F\xDC\x7B\xC0\x22"
+ "\xEB\x7A\x94\x20\x5A\x7B\xD3\x7A\xB9\x5B\xF8\x1A\x5A\x84\x4E\xA1"
+ "\x73\x41\x53\xD2\x60\xF7\x7C\xEE\x68\x59\x85\x80\xFC\x3D\x70\x4B"
+ "\x04\x32\xE7\xF2\xFD\xBD\xB3\xD9\x21\xE2\x37\x56\xA2\x16\xCC\xDE"
+ "\x8A\xD3\xBC\x71\xEF\x58\x19\x0E\x45\x8A\x5B\x53\xD6\x77\x30\x6A"
+ "\xA7\xF8\x68\x06\x4E\x07\xCA\xCE\x30\xD7\x35\xAB\x1A\xC7\x18\xD4"
+ "\xC6\x2F\x1A\xFF\xE9\x7A\x94\x0B\x76\x5E\x7E\x29\x0C\xE6\xD3\x3B"
+ "\x5B\x44\x96\xA8\xF1\x29\x23\x95\xD9\x79\xB3\x39\xFC\x76\xED\xE1"
+ "\x1E\x67\x4E\xF7\xE8\x7B\x7A\x12\x9E\xD8\x4B\x35\x09\x0A\xF2\xC1"
+ "\x63\x5B\xEE\xFD\x2A\xC2\xA6\x66\x30\x3C\x1F\x95\xAF\x65\x22\x95"
+ "\x14\x1D\xF5\xD5\xDC\x38\x79\x35\x1C\xCD\x24\x47\xE0\xFD\x08\xC8"
+ "\xF4\x15\x55\x9F\xD9\xC7\xAC\x3F\x67\xB3\x4F\xEB\x26\x7C\x8E\xD6"
+ "\x74\xB3\x0A\xCD\xE7\xFA\xBE\x7E\xA3\x3E\xEC\x61\x50\x77\x52\x56"
+ "\xCF\x90\x5D\x48\xFB\xD4\x2C\x6C\x61\x8B\xDD\x2B\xF5\x92\x1F\x30"
+ "\xBF\x3F\x80\x0D\x31\xDB\xB2\x0B\x7D\x84\xE3\xA6\x42\x7F\x00\x38"
+ "\x44\x02\xC5\xB8\xD9\x58\x29\x9D\x68\x5C\x32\x8B\x76\xAE\xED\x15"
+ "\xF9\x7C\xAE\x7B\xB6\x8E\xD6\x54\x24\xFF\xFA\x87\x05\xEF\x15\x08"
+ "\x5E\x4B\x21\xA2\x2F\x49\xE7\x0F\xC3\xD0\xB9\x49\x22\xEF\xD5\xCA"
+ "\xB2\x11\xF2\x17\xB6\x77\x24\x68\x76\xB2\x07\xF8\x0A\x73\xDD\x65"
+ "\x9C\x75\x64\xF7\xA1\xC6\x23\x08\x84\x72\x3E\x54\x2E\xEB\x9B\x40"
+ "\xA6\x83\x87\xEB\xB5\x00\x40\x4F\xE1\x72\x2A\x59\x3A\x06\x60\x29"
+ "\x7E\x25\x2F\xD8\x80\x40\x8C\x59\xCA\xCF\x8E\x44\xE4\x2D\x84\x7E"
+ "\xCB\xFD\x1E\x3B\xD5\xFF\x9A\xB9\x66\x93\x6D\x5E\xC8\xB7\x13\x26"
+ "\xD6\x38\x1B\x2B\xE1\x87\x96\x05\xD5\xF3\xAB\x68\xF7\x12\x62\x2C"
+ "\x58\xC1\xC9\x85\x3C\x72\xF1\x26\xEE\xC0\x09\x5F\x1D\x4B\xAC\x01"
+ "\x41\xC8\x12\xF8\xF3\x93\x43\x41\xFF\xEC\x0B\x80\xE2\xEE\x20\x85"
+ "\x25\xCD\x6C\x30\x8C\x0D\x24\x2E\xBA\x19\xEA\x28\x7F\xCF\xD5\x10"
+ "\x5C\xE9\xB2\x9D\x5F\x16\xE4\xC0\xF3\xCC\xD9\x68\x4A\x05\x08\x70"
+ "\x17\x26\xC8\x5C\x4A\xBF\x94\x6A\x0E\xD5\xDA\x67\x47\x4B\xAF\x44"
+ "\xE3\x94\xAA\x05\xDB\xA2\x49\x74\xFA\x5C\x69\xAB\x44\xB7\xF7\xBA"
+ "\xAE\x7A\x23\x87\xEB\x54\x7E\x80\xF1\x5B\x60\xA5\x93\xE5\xD4\x24"
+ "\x84\xF7\x0A\x16\x10\xBE\xE9\x4D\xD8\x6B\x15\x40\x5D\x74\xDA\x1B"
+ "\xFF\x2E\x4D\x17\x9D\x35\xF7\x0D\xCF\x66\x38\x0D\x8A\xE4\xDD\x6B"
+ "\xE1\x0F\x1F\xBD\xFD\x4F\x30\x37\x3F\x96\xB4\x92\x54\xD3\x9A\x7A"
+ "\xD1\x5B\x5B\xA9\x54\x16\xE6\x24\xAB\xD4\x23\x39\x7D\xD2\xC7\x09"
+ "\xFA\xD4\x86\x55\x4D\x60\xC2\x87\x67\x6B\xE6"
};
static void
@@ -686,7 +710,7 @@ test_pac_ticket_signature(krb5_context context)
{
krb5_error_code ret;
krb5_ticket *ticket;
- krb5_principal sprinc;
+ krb5_principal cprinc, sprinc;
krb5_authdata **authdata1, **authdata2;
krb5_pac pac, pac2, pac3;
uint32_t *list;
@@ -701,7 +725,13 @@ test_pac_ticket_signature(krb5_context context)
if (ret)
err(context, ret, "while decrypting ticket");
- ret = krb5_parse_name(context, "s1@CDOM.COM", &sprinc);
+ ret = krb5_parse_name(context, "administrator@W2022-L7.BASE", &cprinc);
+ if (ret)
+ err(context, ret, "krb5_parse_name");
+
+ ret = krb5_parse_name(context,
+ "cifs/w2022-118.w2022-l7.base@W2022-L7.BASE",
+ &sprinc);
if (ret)
err(context, ret, "krb5_parse_name");
@@ -713,7 +743,7 @@ test_pac_ticket_signature(krb5_context context)
/* In this test, the server is also the client. */
ret = krb5_pac_verify(context, pac, ticket->enc_part2->times.authtime,
- ticket->server, NULL, NULL);
+ cprinc, NULL, NULL);
if (ret)
err(context, ret, "while verifying PAC client info");
@@ -722,7 +752,7 @@ test_pac_ticket_signature(krb5_context context)
ticket->enc_part2->authorization_data = NULL;
ret = krb5_kdc_sign_ticket(context, ticket->enc_part2, pac, sprinc,
- sprinc, &ticket_sig_server_key,
+ cprinc, &ticket_sig_server_key,
&ticket_sig_krbtgt_key, FALSE);
if (ret)
err(context, ret, "while signing ticket");
@@ -781,6 +811,7 @@ test_pac_ticket_signature(krb5_context context)
krb5_pac_free(context, pac);
krb5_pac_free(context, pac2);
krb5_pac_free(context, pac3);
+ krb5_free_principal(context, cprinc);
krb5_free_principal(context, sprinc);
krb5_free_ticket(context, ticket);
}
diff --git a/src/tests/t_authdata.py b/src/tests/t_authdata.py
index 4fbdbec052..b0666c3b81 100644
--- a/src/tests/t_authdata.py
+++ b/src/tests/t_authdata.py
@@ -11,7 +11,7 @@ realm = K5Realm(krb5_conf=conf)
# container.
mark('baseline authdata')
out = realm.run(['./adata', realm.host_princ])
-if '?512: ' not in out or '^-42: Hello' not in out:
+if '?128: [6, 7, 10, 16, 19]' not in out or '^-42: Hello' not in out:
fail('expected authdata not seen for basic request')
# Requested authdata is copied into the ticket, with KDC-only types
@@ -239,6 +239,9 @@ realm.run(['./s4u2proxy', usercache, 'service/2'])
out = realm.run(['./adata', '-p', realm.user_princ, 'service/2'])
if '+97: [indcl]' not in out or '[inds1]' in out:
fail('correct auth-indicator not seen for S4U2Proxy req')
+# Make sure a PAC with an S4U_DELEGATION_INFO(11) buffer is included.
+if '?128: [1, 6, 7, 10, 11, 16, 19]' not in out:
+ fail('PAC with delegation info not seen for S4U2Proxy req')
# Get another S4U2Proxy ticket including request-authdata.
realm.run(['./s4u2proxy', usercache, 'service/2', '-2', 'proxy_ad'])
--
2.39.2

View File

@ -0,0 +1,840 @@
From 48be25aaa27487fcbbba76044083de37211b30e7 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Fri, 7 Jan 2022 13:46:24 -0500
Subject: [PATCH] Add PAC ticket signature APIs
Microsoft added a third PAC signature over the ticket to prevent
servers from setting the forwardable flag on evidence tickets. Add
new APIs to generate and verify ticket signatures, as well as defines
for this and other new PAC buffer types. Deprecate the old signing
functions as they cannot generate ticket signatures. Modify several
error returns to better match the protocol errors generated by Active
Directory.
[ghudson@mit.edu: adjusted contracts for KDC requirements; simplified
and commented code changes; wrote commit message. rharwood@redhat.com
also did some work on this commit.]
ticket: 9043 (new)
---
doc/appdev/refs/api/index.rst | 2 +
doc/appdev/refs/macros/index.rst | 6 +
src/include/krb5/krb5.hin | 98 +++++++++++------
src/lib/krb5/krb/deps | 5 +-
src/lib/krb5/krb/int-proto.h | 12 ++
src/lib/krb5/krb/pac.c | 148 ++++++++++++++++++++++++-
src/lib/krb5/krb/pac_sign.c | 121 ++++++++++++++++++++
src/lib/krb5/krb/t_pac.c | 182 +++++++++++++++++++++++++++++++
src/lib/krb5/libkrb5.exports | 2 +
src/lib/krb5_32.def | 3 +
src/plugins/kdb/test/kdb_test.c | 6 +-
11 files changed, 544 insertions(+), 41 deletions(-)
diff --git a/doc/appdev/refs/api/index.rst b/doc/appdev/refs/api/index.rst
index 9e03fd386f..d12be47c3c 100644
--- a/doc/appdev/refs/api/index.rst
+++ b/doc/appdev/refs/api/index.rst
@@ -223,6 +223,8 @@ Rarely used public interfaces
krb5_init_creds_step.rst
krb5_init_keyblock.rst
krb5_is_referral_realm.rst
+ krb5_kdc_sign_ticket.rst
+ krb5_kdc_verify_ticket.rst
krb5_kt_add_entry.rst
krb5_kt_end_seq_get.rst
krb5_kt_get_entry.rst
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index 001fb386a7..c6ea088742 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -234,12 +234,18 @@ Public
KRB5_NT_UNKNOWN.rst
KRB5_NT_WELLKNOWN.rst
KRB5_NT_X500_PRINCIPAL.rst
+ KRB5_PAC_ATTRIBUTES_INFO.rst
KRB5_PAC_CLIENT_INFO.rst
+ KRB5_PAC_CLIENT_CLAIMS.rst
KRB5_PAC_CREDENTIALS_INFO.rst
KRB5_PAC_DELEGATION_INFO.rst
+ KRB5_PAC_DEVICE_CLAIMS.rst
+ KRB5_PAC_DEVICE_INFO.rst
KRB5_PAC_LOGON_INFO.rst
KRB5_PAC_PRIVSVR_CHECKSUM.rst
+ KRB5_PAC_REQUESTOR.rst
KRB5_PAC_SERVER_CHECKSUM.rst
+ KRB5_PAC_TICKET_CHECKSUM.rst
KRB5_PAC_UPN_DNS_INFO.rst
KRB5_PADATA_AFS3_SALT.rst
KRB5_PADATA_AP_REQ.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index a7060aa733..8e59628bd9 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -1918,7 +1918,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
#define KRB5_AUTHDATA_CAMMAC 96
#define KRB5_AUTHDATA_WIN2K_PAC 128
#define KRB5_AUTHDATA_ETYPE_NEGOTIATION 129 /**< RFC 4537 */
-#define KRB5_AUTHDATA_SIGNTICKET 512 /**< formerly 142 in krb5 1.8 */
+#define KRB5_AUTHDATA_SIGNTICKET 512 /**< @deprecated use PAC */
#define KRB5_AUTHDATA_FX_ARMOR 71
#define KRB5_AUTHDATA_AUTH_INDICATOR 97
#define KRB5_AUTHDATA_AP_OPTIONS 143
@@ -8181,6 +8181,12 @@ krb5_verify_authdata_kdc_issued(krb5_context context,
#define KRB5_PAC_CLIENT_INFO 10 /**< Client name and ticket info */
#define KRB5_PAC_DELEGATION_INFO 11 /**< Constrained delegation info */
#define KRB5_PAC_UPN_DNS_INFO 12 /**< User principal name and DNS info */
+#define KRB5_PAC_CLIENT_CLAIMS 13 /**< Client claims information */
+#define KRB5_PAC_DEVICE_INFO 14 /**< Device information */
+#define KRB5_PAC_DEVICE_CLAIMS 15 /**< Device claims information */
+#define KRB5_PAC_TICKET_CHECKSUM 16 /**< Ticket checksum */
+#define KRB5_PAC_ATTRIBUTES_INFO 17 /**< PAC attributes */
+#define KRB5_PAC_REQUESTOR 18 /**< PAC requestor SID */
struct krb5_pac_data;
/** PAC data structure to convey authorization information */
@@ -8338,56 +8344,84 @@ krb5_pac_verify_ext(krb5_context context, const krb5_pac pac,
krb5_boolean with_realm);
/**
- * Sign a PAC.
+ * Verify a PAC, possibly including ticket signature
*
- * @param [in] context Library context
- * @param [in] pac PAC handle
- * @param [in] authtime Expected timestamp
- * @param [in] principal Expected principal name (or NULL)
- * @param [in] server_key Key for server checksum
- * @param [in] privsvr_key Key for KDC checksum
- * @param [out] data Signed PAC encoding
+ * @param [in] context Library context
+ * @param [in] enc_tkt Ticket enc-part, possibly containing a PAC
+ * @param [in] server_princ Canonicalized name of ticket server
+ * @param [in] server Key to validate server checksum (or NULL)
+ * @param [in] privsvr Key to validate KDC checksum (or NULL)
+ * @param [out] pac_out Verified PAC (NULL if no PAC included)
*
- * This function signs @a pac using the keys @a server_key and @a privsvr_key
- * and returns the signed encoding in @a data. @a pac is modified to include
- * the server and KDC checksum buffers. Use krb5_free_data_contents() to free
- * @a data when it is no longer needed.
+ * If a PAC is present in @a enc_tkt, verify its signatures. If @a privsvr is
+ * not NULL and @a server_princ is not a krbtgt or kadmin/changepw service,
+ * require a ticket signature over @a enc_tkt in addition to the KDC signature.
+ * Place the verified PAC in @a pac_out. If an invalid PAC signature is found,
+ * return an error matching the Windows KDC protocol code for that condition as
+ * closely as possible.
*
- * @version New in 1.10
+ * If no PAC is present in @a enc_tkt, set @a pac_out to NULL and return
+ * successfully.
+ *
+ * @note This function does not validate the PAC_CLIENT_INFO buffer. If a
+ * specific value is expected, the caller can make a separate call to
+ * krb5_pac_verify_ext() with a principal but no keys.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ *
+ * @version New in 1.20
*/
krb5_error_code KRB5_CALLCONV
+krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
+ krb5_const_principal server_princ,
+ const krb5_keyblock *server,
+ const krb5_keyblock *privsvr, krb5_pac *pac_out);
+
+/** @deprecated Use krb5_kdc_sign_ticket() instead. */
+krb5_error_code KRB5_CALLCONV
krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
krb5_const_principal principal, const krb5_keyblock *server_key,
const krb5_keyblock *privsvr_key, krb5_data *data);
+/** @deprecated Use krb5_kdc_sign_ticket() instead. */
+krb5_error_code KRB5_CALLCONV
+krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
+ krb5_data *data);
+
/**
- * Sign a PAC, possibly with a specified realm.
+ * Sign a PAC, possibly including a ticket signature
*
* @param [in] context Library context
+ * @param [in] enc_tkt The ticket for the signature
* @param [in] pac PAC handle
- * @param [in] authtime Expected timestamp
- * @param [in] principal Principal name (or NULL)
- * @param [in] server_key Key for server checksum
- * @param [in] privsvr_key Key for KDC checksum
+ * @param [in] server_princ Canonical ticket server name
+ * @param [in] client_princ PAC_CLIENT_INFO principal (or NULL)
+ * @param [in] server Key for server checksum
+ * @param [in] privsvr Key for KDC and ticket checksum
* @param [in] with_realm If true, include the realm of @a principal
- * @param [out] data Signed PAC encoding
*
- * This function is similar to krb5_pac_sign(), but adds a parameter
- * @a with_realm. If @a with_realm is true, the PAC_CLIENT_INFO field of the
- * signed PAC will include the realm of @a principal as well as the name. This
- * flag is necessary to generate PACs for cross-realm S4U2Self referrals.
+ * Sign @a pac using the keys @a server and @a privsvr. Include a ticket
+ * signature over @a enc_tkt if @a server_princ is not a TGS or kadmin/changepw
+ * principal name. Add the signed PAC's encoding to the authorization data of
+ * @a enc_tkt in the first slot, wrapped in an AD-IF-RELEVANT container. If @a
+ * client_princ is non-null, add a PAC_CLIENT_INFO buffer, including the realm
+ * if @a with_realm is true.
*
- * @version New in 1.17
+ * @retval 0 on success, otherwise - Kerberos error codes
+ *
+ * @version New in 1.20
*/
krb5_error_code KRB5_CALLCONV
-krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
- krb5_const_principal principal,
- const krb5_keyblock *server_key,
- const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
- krb5_data *data);
+krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
+ const krb5_pac pac, krb5_const_principal server_princ,
+ krb5_const_principal client_princ,
+ const krb5_keyblock *server, const krb5_keyblock *privsvr,
+ krb5_boolean with_realm);
-
-/*
+/**
* Read client information from a PAC.
*
* @param [in] context Library context
diff --git a/src/lib/krb5/krb/deps b/src/lib/krb5/krb/deps
index 439ca02725..cd842b03cd 100644
--- a/src/lib/krb5/krb/deps
+++ b/src/lib/krb5/krb/deps
@@ -709,7 +709,7 @@ pac.so pac.po $(OUTPRE)pac.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/k5-utf8.h $(top_srcdir)/include/krb5.h \
$(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
- authdata.h pac.c
+ authdata.h int-proto.h pac.c
pac_sign.so pac_sign.po $(OUTPRE)pac_sign.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -720,7 +720,8 @@ pac_sign.so pac_sign.po $(OUTPRE)pac_sign.$(OBJEXT): \
$(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/k5-utf8.h \
$(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
$(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h authdata.h pac_sign.c
+ $(top_srcdir)/include/socket-utils.h authdata.h int-proto.h \
+ pac_sign.c
padata.so padata.po $(OUTPRE)padata.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index fe61bebf5b..453ed60c6c 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -386,4 +386,16 @@ k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
krb5_ccache ccache, krb5_creds *in_creds,
krb5_creds **out_creds);
+/* Return true if mprinc will match any hostname in a host-based principal name
+ * (possibly due to ignore_acceptor_hostname) with krb5_sname_match(). */
+krb5_boolean
+k5_sname_wildcard_host(krb5_context context, krb5_const_principal mprinc);
+
+/* Guess the appropriate name-type for a principal based on the name. */
+krb5_int32
+k5_infer_principal_type(krb5_principal princ);
+
+krb5_boolean
+k5_pac_should_have_ticket_signature(krb5_const_principal sprinc);
+
#endif /* KRB5_INT_FUNC_PROTO__ */
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 1b9ef12276..6eb23d8090 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -25,6 +25,7 @@
*/
#include "k5-int.h"
+#include "int-proto.h"
#include "authdata.h"
#define MAX_BUFFERS 4096
@@ -552,8 +553,10 @@ k5_pac_verify_server_checksum(krb5_context context,
checksum.checksum_type = load_32_le(p);
checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
+ if (checksum.checksum_type == CKSUMTYPE_SHA1)
+ return KRB5KDC_ERR_SUMTYPE_NOSUPP;
if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
- return KRB5KRB_AP_ERR_INAPP_CKSUM;
+ return KRB5KRB_ERR_GENERIC;
pac_data.length = pac->data.length;
pac_data.data = k5memdup(pac->data.data, pac->data.length, &ret);
@@ -586,7 +589,7 @@ k5_pac_verify_server_checksum(krb5_context context,
}
if (valid == FALSE)
- ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ ret = KRB5KRB_AP_ERR_MODIFIED;
return ret;
}
@@ -623,7 +626,7 @@ k5_pac_verify_kdc_checksum(krb5_context context,
checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
- return KRB5KRB_AP_ERR_INAPP_CKSUM;
+ return KRB5KRB_ERR_GENERIC;
server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
@@ -635,11 +638,148 @@ k5_pac_verify_kdc_checksum(krb5_context context,
return ret;
if (valid == FALSE)
- ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ ret = KRB5KRB_AP_ERR_MODIFIED;
return ret;
}
+static krb5_error_code
+verify_ticket_checksum(krb5_context context, const krb5_pac pac,
+ const krb5_data *ticket, const krb5_keyblock *privsvr)
+{
+ krb5_error_code ret;
+ krb5_checksum checksum;
+ krb5_data checksum_data;
+ krb5_boolean valid;
+ krb5_octet *p;
+
+ ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_TICKET_CHECKSUM,
+ &checksum_data);
+ if (ret != 0)
+ return KRB5KRB_AP_ERR_MODIFIED;
+
+ if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+
+ p = (krb5_octet *)checksum_data.data;
+ checksum.checksum_type = load_32_le(p);
+ checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
+ checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
+ if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
+ return KRB5KRB_ERR_GENERIC;
+
+ ret = krb5_c_verify_checksum(context, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, ticket,
+ &checksum, &valid);
+ if (ret != 0)
+ return ret;
+
+ return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
+}
+
+/* Per MS-PAC 2.8.3, tickets encrypted to TGS and password change principals
+ * should not have ticket signatures. */
+krb5_boolean
+k5_pac_should_have_ticket_signature(krb5_const_principal sprinc)
+{
+ if (IS_TGS_PRINC(sprinc))
+ return FALSE;
+ if (sprinc->length == 2 && data_eq_string(sprinc->data[0], "kadmin") &&
+ data_eq_string(sprinc->data[1], "changepw"))
+ return FALSE;
+ return TRUE;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
+ krb5_const_principal server_princ,
+ const krb5_keyblock *server,
+ const krb5_keyblock *privsvr, krb5_pac *pac_out)
+{
+ krb5_error_code ret;
+ krb5_pac pac = NULL;
+ krb5_data *recoded_tkt = NULL;
+ krb5_authdata **authdata, *orig, **ifrel = NULL, **recoded_ifrel = NULL;
+ uint8_t z = 0;
+ krb5_authdata zpac = { KV5M_AUTHDATA, KRB5_AUTHDATA_WIN2K_PAC, 1, &z };
+ size_t i, j;
+
+ *pac_out = NULL;
+
+ /*
+ * Find the position of the PAC in the ticket authdata. ifrel will be the
+ * decoded AD-IF-RELEVANT container at position i containing a PAC, and j
+ * will be the offset within the container.
+ */
+ authdata = enc_tkt->authorization_data;
+ for (i = 0; authdata != NULL && authdata[i] != NULL; i++) {
+ if (authdata[i]->ad_type != KRB5_AUTHDATA_IF_RELEVANT)
+ continue;
+
+ ret = krb5_decode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ authdata[i], &ifrel);
+ if (ret)
+ goto cleanup;
+
+ for (j = 0; ifrel[j] != NULL; j++) {
+ if (ifrel[j]->ad_type == KRB5_AUTHDATA_WIN2K_PAC)
+ break;
+ }
+ if (ifrel[j] != NULL)
+ break;
+
+ krb5_free_authdata(context, ifrel);
+ ifrel = NULL;
+ }
+
+ /* Stop and return successfully if we didn't find a PAC. */
+ if (ifrel == NULL) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ ret = krb5_pac_parse(context, ifrel[j]->contents, ifrel[j]->length, &pac);
+ if (ret)
+ goto cleanup;
+
+ if (privsvr != NULL && k5_pac_should_have_ticket_signature(server_princ)) {
+ /* To check the PAC ticket signatures, re-encode the ticket with the
+ * PAC contents replaced by a single zero. */
+ orig = ifrel[j];
+ ifrel[j] = &zpac;
+ ret = krb5_encode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ ifrel, &recoded_ifrel);
+ ifrel[j] = orig;
+ if (ret)
+ goto cleanup;
+ orig = authdata[i];
+ authdata[i] = recoded_ifrel[0];
+ ret = encode_krb5_enc_tkt_part(enc_tkt, &recoded_tkt);
+ authdata[i] = orig;
+ if (ret)
+ goto cleanup;
+
+ ret = verify_ticket_checksum(context, pac, recoded_tkt, privsvr);
+ if (ret)
+ goto cleanup;
+ }
+
+ ret = krb5_pac_verify_ext(context, pac, enc_tkt->times.authtime, NULL,
+ server, privsvr, FALSE);
+
+ *pac_out = pac;
+ pac = NULL;
+
+cleanup:
+ krb5_pac_free(context, pac);
+ krb5_free_data(context, recoded_tkt);
+ krb5_free_authdata(context, ifrel);
+ krb5_free_authdata(context, recoded_ifrel);
+ return ret;
+}
+
krb5_error_code KRB5_CALLCONV
krb5_pac_verify(krb5_context context,
const krb5_pac pac,
diff --git a/src/lib/krb5/krb/pac_sign.c b/src/lib/krb5/krb/pac_sign.c
index 12f0259b4f..0f9581abbb 100644
--- a/src/lib/krb5/krb/pac_sign.c
+++ b/src/lib/krb5/krb/pac_sign.c
@@ -25,6 +25,7 @@
*/
#include "k5-int.h"
+#include "int-proto.h"
#include "authdata.h"
/* draft-brezak-win2k-krb-authz-00 */
@@ -286,3 +287,123 @@ krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
return 0;
}
+
+/* Add a signature over der_enc_tkt in privsvr to pac. der_enc_tkt should be
+ * encoded with a dummy PAC authdata element containing a single zero byte. */
+static krb5_error_code
+add_ticket_signature(krb5_context context, const krb5_pac pac,
+ krb5_data *der_enc_tkt, const krb5_keyblock *privsvr)
+{
+ krb5_error_code ret;
+ krb5_data ticket_cksum;
+ krb5_cksumtype ticket_cksumtype;
+ krb5_crypto_iov iov[2];
+
+ /* Create zeroed buffer for checksum. */
+ ret = k5_insert_checksum(context, pac, KRB5_PAC_TICKET_CHECKSUM,
+ privsvr, &ticket_cksumtype);
+ if (ret)
+ return ret;
+
+ ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_TICKET_CHECKSUM,
+ &ticket_cksum);
+ if (ret)
+ return ret;
+
+ iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[0].data = *der_enc_tkt;
+ iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
+ iov[1].data = make_data(ticket_cksum.data + PAC_SIGNATURE_DATA_LENGTH,
+ ticket_cksum.length - PAC_SIGNATURE_DATA_LENGTH);
+ ret = krb5_c_make_checksum_iov(context, ticket_cksumtype, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, iov, 2);
+ if (ret)
+ return ret;
+
+ store_32_le(ticket_cksumtype, ticket_cksum.data);
+ return 0;
+}
+
+/* Set *out to an AD-IF-RELEVANT authdata element containing a PAC authdata
+ * element with contents pac_data. */
+static krb5_error_code
+encode_pac_ad(krb5_context context, krb5_data *pac_data, krb5_authdata **out)
+{
+ krb5_error_code ret;
+ krb5_authdata *container[2], **encoded_container = NULL;
+ krb5_authdata pac_ad = { KV5M_AUTHDATA, KRB5_AUTHDATA_WIN2K_PAC };
+ uint8_t z = 0;
+
+ pac_ad.contents = (pac_data != NULL) ? (uint8_t *)pac_data->data : &z;
+ pac_ad.length = (pac_data != NULL) ? pac_data->length : 1;
+ container[0] = &pac_ad;
+ container[1] = NULL;
+
+ ret = krb5_encode_authdata_container(context, KRB5_AUTHDATA_IF_RELEVANT,
+ container, &encoded_container);
+ if (ret)
+ return ret;
+
+ *out = encoded_container[0];
+ free(encoded_container);
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
+ const krb5_pac pac, krb5_const_principal server_princ,
+ krb5_const_principal client_princ,
+ const krb5_keyblock *server, const krb5_keyblock *privsvr,
+ krb5_boolean with_realm)
+{
+ krb5_error_code ret;
+ krb5_data *der_enc_tkt = NULL, pac_data = empty_data();
+ krb5_authdata **list, *pac_ad;
+ size_t count;
+
+ /* Reallocate space for another authdata element in enc_tkt. */
+ list = enc_tkt->authorization_data;
+ for (count = 0; list != NULL && list[count] != NULL; count++);
+ list = realloc(enc_tkt->authorization_data, (count + 2) * sizeof(*list));
+ if (list == NULL)
+ return ENOMEM;
+ list[count] = NULL;
+ enc_tkt->authorization_data = list;
+
+ /* Create a dummy PAC for ticket signing and make it the first element. */
+ ret = encode_pac_ad(context, NULL, &pac_ad);
+ if (ret)
+ goto cleanup;
+ memmove(list + 1, list, (count + 1) * sizeof(*list));
+ list[0] = pac_ad;
+
+ if (k5_pac_should_have_ticket_signature(server_princ)) {
+ ret = encode_krb5_enc_tkt_part(enc_tkt, &der_enc_tkt);
+ if (ret)
+ goto cleanup;
+
+ assert(privsvr != NULL);
+ ret = add_ticket_signature(context, pac, der_enc_tkt, privsvr);
+ if (ret)
+ goto cleanup;
+ }
+
+ ret = krb5_pac_sign_ext(context, pac, enc_tkt->times.authtime,
+ client_princ, server, privsvr, with_realm,
+ &pac_data);
+ if (ret)
+ goto cleanup;
+
+ /* Replace the dummy PAC with the signed real one. */
+ ret = encode_pac_ad(context, &pac_data, &pac_ad);
+ if (ret)
+ goto cleanup;
+ free(list[0]->contents);
+ free(list[0]);
+ list[0] = pac_ad;
+
+cleanup:
+ krb5_free_data(context, der_enc_tkt);
+ krb5_free_data_contents(context, &pac_data);
+ return ret;
+}
diff --git a/src/lib/krb5/krb/t_pac.c b/src/lib/krb5/krb/t_pac.c
index ccd165380d..173bde7bab 100644
--- a/src/lib/krb5/krb/t_pac.c
+++ b/src/lib/krb5/krb/t_pac.c
@@ -605,6 +605,186 @@ check_pac(krb5_context context, int index, const unsigned char *pdata,
krb5_pac_free(context, pac);
}
+static const krb5_keyblock ticket_sig_krbtgt_key = {
+ 0, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ 32, U("\x7a\x58\x98\xd2\xaf\xa6\xaf\xc0\x6a\xce\x06\x04\x4b\xc2\x70\x84"
+ "\x9b\x8e\x0a\x6c\x4c\x07\xdc\x6f\xbb\x48\x43\xe1\xd2\xaa\x97\xf7")
+};
+
+static const krb5_keyblock ticket_sig_server_key = {
+ 0, ENCTYPE_ARCFOUR_HMAC,
+ 16, U("\xed\x23\x11\x20\x7a\x21\x44\x20\xbf\xc0\x8d\x36\xf7\xf6\xb2\x3e")
+};
+
+static const krb5_data ticket_data = {
+ .length = 972, .data =
+ "\x61\x82\x03\xC8\x30\x82\x03\xC4\xA0\x03\x02\x01\x05\xA1\x0A\x1B"
+ "\x08\x43\x44\x4F\x4D\x2E\x43\x4F\x4D\xA2\x0F\x30\x0D\xA0\x03\x02"
+ "\x01\x01\xA1\x06\x30\x04\x1B\x02\x73\x31\xA3\x82\x03\x9E\x30\x82"
+ "\x03\x9A\xA0\x03\x02\x01\x17\xA1\x03\x02\x01\x03\xA2\x82\x03\x8C"
+ "\x04\x82\x03\x88\x44\x31\x61\x20\x17\xC9\xFE\xBC\xAC\x46\xB5\x77"
+ "\xE9\x68\x04\x4C\x9B\x31\x91\x0C\xC1\xD4\xDD\xEF\xC7\x34\x20\x08"
+ "\x90\x91\xE8\x79\xE0\xB5\x03\x26\xA4\x65\xDE\xEC\x47\x03\x2A\x8F"
+ "\x61\xE7\x4D\x38\x5A\x42\x95\x5A\xF9\x2F\x41\x2C\x2A\x6E\x60\xA1"
+ "\xEB\x51\xB3\xBD\x4C\x00\x41\x2A\x44\x76\x08\x37\x1A\x51\xFD\x65"
+ "\x67\x7E\xBF\x3D\x90\x86\xE3\x9A\x54\x6B\x67\xA8\x08\x7A\x73\xCC"
+ "\xC3\xB7\x4B\xD5\x5C\x3A\x14\x6C\xC1\x5F\x54\x4B\x92\x55\xB4\xB7"
+ "\x92\x23\x3F\x53\x89\x47\x8E\x1F\x8B\xB9\xDB\x3B\x93\xE8\x70\xE4"
+ "\x24\xB8\x9D\xF0\x0E\x35\x28\xF8\x7A\x27\x5D\xF7\x25\x97\x9C\xF5"
+ "\x9F\x9F\x64\x04\xF2\xA3\xAB\x11\x15\xB6\xDA\x18\xD6\x46\xD5\xE6"
+ "\xB8\x08\xDE\x0A\x62\xFD\xF8\xAA\x52\x90\xD9\x67\x29\xB2\xCD\x06"
+ "\xB6\xB0\x50\x2B\x3F\x0F\xA3\xA5\xBF\xAA\x6E\x40\x03\xD6\x5F\x02"
+ "\xBC\xD8\x18\x47\x97\x09\xD7\xE4\x96\x3B\xCB\xEB\x92\x2C\x3C\x49"
+ "\xFF\x1F\x71\xE0\x52\x94\x0F\x8B\x9F\xB8\x2A\xBB\x9C\xE2\xA3\xDD"
+ "\x38\x89\xE2\xB1\x0B\x9E\x1F\x7A\xB3\xE3\xD2\xB0\x94\xDC\x87\xBE"
+ "\x37\xA6\xD3\xB3\x29\x35\x9A\x72\xC3\x7A\xF1\xA9\xE6\xC5\xD1\x26"
+ "\x83\x65\x44\x17\xBA\x55\xA8\x5E\x94\x26\xED\xE9\x8A\x93\x11\x5D"
+ "\x7E\x20\x1B\x9C\x15\x9E\x13\x37\x03\x4D\xDD\x99\x51\xD8\x66\x29"
+ "\x6A\xB9\xFB\x49\xFE\x52\x78\xDA\x86\x85\xA9\xA3\xB9\xEF\xEC\xAD"
+ "\x35\xA6\x8D\xAC\x0F\x75\x22\xBB\x0B\x49\x1C\x13\x52\x40\xC9\x52"
+ "\x69\x09\x54\xD1\x0F\x94\x3F\x22\x48\x67\xB0\x96\x28\xAA\xE6\x28"
+ "\xD9\x0C\x08\xEF\x51\xED\x15\x5E\xA2\x53\x59\xA5\x03\xB4\x06\x20"
+ "\x3D\xCC\xB4\xC5\xF8\x8C\x73\x67\xA3\x21\x3D\x19\xCD\xD4\x12\x28"
+ "\xD2\x93\xDE\x0D\xF0\x71\x10\x50\xD6\x33\x35\x04\x11\x64\x43\x39"
+ "\xC3\xDF\x96\xE3\x66\xE3\x85\xCA\xE7\x67\x14\x3A\xF0\x43\xAA\xBB"
+ "\xD4\x1D\xB5\x24\xB5\x74\x90\x25\xA7\x87\x7E\xDB\xD3\x83\x8A\x3A"
+ "\x69\xA8\x2D\xAF\xB7\xB8\xF3\xDC\x13\xAF\x45\x61\x3F\x59\x39\x7E"
+ "\x69\xDE\x0C\x04\xF1\x10\x6B\xB4\x56\xFA\x21\x9F\x72\x2B\x60\x86"
+ "\xE3\x23\x0E\xC4\x51\xF6\xBE\xD8\xE1\x5F\xEE\x73\x4C\x17\x4C\x2C"
+ "\x1B\xFB\x9F\x1F\x7A\x3B\x07\x5B\x8E\xF1\x01\xAC\xD6\x30\x94\x8A"
+ "\x5D\x22\x6F\x08\xCE\xED\x5E\xB6\xDB\x86\x8C\x87\xEB\x8D\x91\xFF"
+ "\x0A\x86\x30\xBD\xC0\xF8\x25\xE7\xAE\x24\x35\xF2\xFC\xE5\xFD\x1B"
+ "\xB0\x05\x4A\xA3\xE5\xEB\x2E\x05\xAD\x99\x67\x49\x87\xE6\xB3\x87"
+ "\x82\xA4\x59\xA7\x6E\xDD\xF2\xB6\x66\xE8\xF7\x70\xF5\xBD\xC9\x0E"
+ "\xFA\x9C\x79\x84\xD4\x9B\x05\x0E\xBB\xF5\xDB\xEF\xFC\xCC\x26\xF2"
+ "\x93\xCF\xD2\x04\x3C\xA9\x2C\x65\x42\x97\x86\xD8\x38\x0A\x1E\xF6"
+ "\xD6\xCA\x30\xB5\x1A\xEC\xFB\xBA\x3B\x84\x57\xB0\xFD\xFB\xE6\xBC"
+ "\xF2\x76\xF6\x4C\xBB\xAB\xB1\x31\xA1\x27\x7C\xE6\xE6\x81\xB6\xCE"
+ "\x84\x86\x40\xB6\x40\x33\xC4\xF8\xB4\x15\xCF\xAA\xA5\x51\x78\xB9"
+ "\x8B\x50\x25\xB2\x88\x86\x96\x72\x8C\x71\x4D\xB5\x3A\x94\x86\x77"
+ "\x0E\x95\x9B\x16\x93\xEF\x3A\x11\x79\xBA\x83\xF7\x74\xD3\x8D\xBA"
+ "\x15\xE1\x2C\x04\x57\xA8\x92\x1E\x9D\x00\x8E\x20\xFD\x30\x70\xE7"
+ "\xF5\x65\x2F\x19\x0C\x94\xBA\x03\x71\x12\x96\xCD\xC8\xB4\x96\xDB"
+ "\xCE\x19\xC2\xDF\x3C\xC2\xF6\x3D\x53\xED\x98\xA5\x41\x72\x2A\x22"
+ "\x7B\xF3\x2B\x17\x6C\xE1\x39\x7D\xAE\x9B\x11\xF9\xC1\xA6\x9E\x9F"
+ "\x89\x3C\x12\xAA\x94\x74\xA7\x4F\x70\xE8\xB9\xDE\x04\xF0\x9D\x39"
+ "\x24\x2D\x92\xE8\x46\x2D\x2E\xF0\x40\x66\x1A\xD9\x27\xF9\x98\xF1"
+ "\x81\x1D\x70\x62\x63\x30\x6D\xCD\x84\x04\x5F\xFA\x83\xD3\xEC\x8D"
+ "\x86\xFB\x40\x61\xC1\x8A\x45\xFF\x7B\xD9\xD4\x18\x61\x7F\x51\xE3"
+ "\xFC\x1E\x18\xF0\xAF\xC6\x18\x2C\xE1\x6D\x5D\xF9\x62\xFC\x20\xA3"
+ "\xB2\x8A\x5F\xE5\xBB\x29\x0F\x99\x63\x07\x88\x38\x3A\x3B\x73\x2A"
+ "\x6D\xDA\x3D\xA8\x0D\x8F\x56\x41\x89\x82\xE5\xB8\x61\x00\x64\x7D"
+ "\x17\x0C\xCE\x03\x55\x8F\xF4\x5B\x0D\x50\xF2\xEB\x05\x67\xBE\xDB"
+ "\x7B\x75\xC5\xEA\xA1\xAB\x1D\xB0\x3C\x6D\x42\x08\x0B\x9A\x45\x20"
+ "\xA8\x8F\xE5\x67\x47\x30\xDE\x93\x5F\x43\x05\xEB\xA8\x2D\x80\xF5"
+ "\x1A\xB8\x4A\x4E\x42\x2D\x0B\x7A\xDC\x46\x20\x2D\x13\x17\xDD\x4B"
+ "\x94\x96\xAA\x1F\x06\x0C\x1F\x62\x07\x9C\x40\xA1"
+};
+
+static void
+test_pac_ticket_signature(krb5_context context)
+{
+ krb5_error_code ret;
+ krb5_ticket *ticket;
+ krb5_principal sprinc;
+ krb5_authdata **authdata1, **authdata2;
+ krb5_pac pac, pac2, pac3;
+ uint32_t *list;
+ size_t len, i;
+ krb5_data data;
+
+ ret = krb5_decode_ticket(&ticket_data, &ticket);
+ if (ret)
+ err(context, ret, "while decoding ticket");
+
+ ret = krb5_decrypt_tkt_part(context, &ticket_sig_server_key, ticket);
+ if (ret)
+ err(context, ret, "while decrypting ticket");
+
+ ret = krb5_parse_name(context, "s1@CDOM.COM", &sprinc);
+ if (ret)
+ err(context, ret, "krb5_parse_name");
+
+ ret = krb5_kdc_verify_ticket(context, ticket->enc_part2, sprinc,
+ &ticket_sig_server_key,
+ &ticket_sig_krbtgt_key, &pac);
+ if (ret)
+ err(context, ret, "while verifying ticket");
+
+ /* In this test, the server is also the client. */
+ ret = krb5_pac_verify(context, pac, ticket->enc_part2->times.authtime,
+ ticket->server, NULL, NULL);
+ if (ret)
+ err(context, ret, "while verifying PAC client info");
+
+ /* We know there is only a PAC in this test's ticket. */
+ authdata1 = ticket->enc_part2->authorization_data;
+ ticket->enc_part2->authorization_data = NULL;
+
+ ret = krb5_kdc_sign_ticket(context, ticket->enc_part2, pac, sprinc,
+ sprinc, &ticket_sig_server_key,
+ &ticket_sig_krbtgt_key, FALSE);
+ if (ret)
+ err(context, ret, "while signing ticket");
+
+ authdata2 = ticket->enc_part2->authorization_data;
+ assert(authdata2 != NULL);
+ assert(authdata2[1] == NULL);
+
+ assert(authdata1[0]->length == authdata2[0]->length);
+ assert(memcmp(authdata1[0]->contents, authdata2[0]->contents,
+ authdata1[0]->length) == 0);
+
+ /* Test adding signatures to a new PAC. */
+ ret = krb5_pac_init(context, &pac2);
+ if (ret)
+ err(context, ret, "krb5_pac_init");
+
+ ret = krb5_pac_get_types(context, pac, &len, &list);
+ if (ret)
+ err(context, ret, "krb5_pac_get_types");
+
+ for (i = 0; i < len; i++) {
+ /* Skip server_cksum, privsvr_cksum, and ticket_cksum. */
+ if (list[i] == 6 || list[i] == 7 || list[i] == 16)
+ continue;
+
+ ret = krb5_pac_get_buffer(context, pac, list[i], &data);
+ if (ret)
+ err(context, ret, "krb5_pac_get_buffer");
+
+ ret = krb5_pac_add_buffer(context, pac2, list[i], &data);
+ if (ret)
+ err(context, ret, "krb5_pac_add_buffer");
+
+ krb5_free_data_contents(context, &data);
+ }
+ free(list);
+
+ krb5_free_authdata(context, authdata1);
+ krb5_free_authdata(context, ticket->enc_part2->authorization_data);
+ ticket->enc_part2->authorization_data = NULL;
+
+ ret = krb5_kdc_sign_ticket(context, ticket->enc_part2, pac2, sprinc, NULL,
+ &ticket_sig_server_key, &ticket_sig_krbtgt_key,
+ FALSE);
+ if (ret)
+ err(context, ret, "while signing ticket");
+
+ /* We can't compare the data since the order of the buffers may differ. */
+ ret = krb5_kdc_verify_ticket(context, ticket->enc_part2, sprinc,
+ &ticket_sig_server_key,
+ &ticket_sig_krbtgt_key, &pac3);
+ if (ret)
+ err(context, ret, "while verifying ticket");
+
+ krb5_pac_free(context, pac);
+ krb5_pac_free(context, pac2);
+ krb5_pac_free(context, pac3);
+ krb5_free_principal(context, sprinc);
+ krb5_free_ticket(context, ticket);
+}
+
int
main(int argc, char **argv)
{
@@ -618,6 +798,8 @@ main(int argc, char **argv)
if (ret)
err(NULL, 0, "krb5_init_contex");
+ test_pac_ticket_signature(context);
+
ret = krb5_set_default_realm(context, "WIN2K3.THINKER.LOCAL");
if (ret)
err(context, ret, "krb5_set_default_realm");
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 48ae46f5c4..28784ec67c 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -462,6 +462,8 @@ krb5_is_permitted_enctype
krb5_is_referral_realm
krb5_is_thread_safe
krb5_kdc_rep_decrypt_proc
+krb5_kdc_sign_ticket
+krb5_kdc_verify_ticket
krb5_kt_add_entry
krb5_kt_client_default
krb5_kt_close
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
index 209c6aaef5..8c3469a96c 100644
--- a/src/lib/krb5_32.def
+++ b/src/lib/krb5_32.def
@@ -506,3 +506,6 @@ EXPORTS
; new in 1.20
krb5_marshal_credentials @472
krb5_unmarshal_credentials @473
+ k5_sname_compare @474 ; PRIVATE GSSAPI
+ krb5_kdc_sign_ticket @475 ;
+ krb5_kdc_verify_ticket @476 ;
diff --git a/src/plugins/kdb/test/kdb_test.c b/src/plugins/kdb/test/kdb_test.c
index 38d371cb86..7d033acae4 100644
--- a/src/plugins/kdb/test/kdb_test.c
+++ b/src/plugins/kdb/test/kdb_test.c
@@ -675,7 +675,7 @@ verify_kdc_signature(krb5_context context, krb5_pac pac,
int tries;
ret = krb5_pac_verify(context, pac, 0, NULL, NULL, tgt_key);
- if (ret != KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ if (ret != KRB5KRB_AP_ERR_MODIFIED)
return ret;
kvno = tgt->key_data[0].key_data_kvno - 1;
@@ -684,7 +684,7 @@ verify_kdc_signature(krb5_context context, krb5_pac pac,
for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
if (ret)
- return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ return KRB5KRB_AP_ERR_MODIFIED;
ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &old_key, NULL);
if (ret)
return ret;
@@ -697,7 +697,7 @@ verify_kdc_signature(krb5_context context, krb5_pac pac,
kvno = kd->key_data_kvno - 1;
}
- return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ return KRB5KRB_AP_ERR_MODIFIED;
}
static krb5_error_code
--
2.39.2

View File

@ -1,631 +0,0 @@
From c93112a19f73b9a984cabd320129ee8f70cb4823 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 12 Mar 2018 11:31:46 -0400
Subject: [PATCH] Add PKINIT KDC support for freshness token
Send a freshness token in the preauth hint list if PKINIT is
configured and the request padata indicates support. Verify the
freshness token if the client includes one in a PKINIT request, and
log whether one was received. If pkinit_require_freshness is set to
true in the realm config, reject non-anonymous requests which don't
contain a freshness token.
Add freshness token tests to t_pkinit.py with some related changes.
Remove client long-term keys after testing password preauth so we get
better error reporting when pkinit_require_freshness is set and a
token is not sent. Remove ./responder invocations for test cases
which don't ask PKINIT responder questions, or else the responder
would fail now that it isn't being asked for the password. Leave
anonymous PKINIT enabled after the anonymous tests so that we can use
it again when testing enforcement of pkinit_require_freshness. Add
expected trace messages for the basic test, including one for
receiving a freshness token. Add minimal expected trace messages for
the RSA test.
ticket: 8648
(cherry picked from commit 4a9050df0bc34bfb08ba24462d6e2514640f4b8e)
---
doc/admin/conf_files/kdc_conf.rst | 4 +
doc/admin/pkinit.rst | 25 +++++
doc/appdev/refs/macros/index.rst | 2 +
doc/formats/freshness_token.rst | 19 ++++
doc/formats/index.rst | 1 +
src/include/krb5/kdcpreauth_plugin.h | 17 ++++
src/include/krb5/krb5.hin | 3 +
src/kdc/do_as_req.c | 2 +
src/kdc/kdc_preauth.c | 130 +++++++++++++++++++++++-
src/kdc/kdc_util.h | 2 +
src/plugins/preauth/pkinit/pkinit.h | 2 +
src/plugins/preauth/pkinit/pkinit_srv.c | 51 +++++++++-
src/tests/t_pkinit.py | 50 ++++++---
13 files changed, 292 insertions(+), 16 deletions(-)
create mode 100644 doc/formats/freshness_token.rst
diff --git a/doc/admin/conf_files/kdc_conf.rst b/doc/admin/conf_files/kdc_conf.rst
index 3af1c3796..1ac1a37c2 100644
--- a/doc/admin/conf_files/kdc_conf.rst
+++ b/doc/admin/conf_files/kdc_conf.rst
@@ -798,6 +798,10 @@ For information about the syntax of some of these options, see
**pkinit_require_crl_checking** should be set to true if the
policy is such that up-to-date CRLs must be present for every CA.
+**pkinit_require_freshness**
+ Specifies whether to require clients to include a freshness token
+ in PKINIT requests. The default value is false. (New in release
+ 1.17.)
.. _Encryption_types:
diff --git a/doc/admin/pkinit.rst b/doc/admin/pkinit.rst
index c601c5c9e..bec4fc800 100644
--- a/doc/admin/pkinit.rst
+++ b/doc/admin/pkinit.rst
@@ -327,3 +327,28 @@ appropriate :ref:`kdc_realms` subsection of the KDC's
To obtain anonymous credentials on a client, run ``kinit -n``, or
``kinit -n @REALMNAME`` to specify a realm. The resulting tickets
will have the client name ``WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS``.
+
+
+Freshness tokens
+----------------
+
+Freshness tokens can ensure that the client has recently had access to
+its certificate private key. If freshness tokens are not required by
+the KDC, a client program with temporary possession of the private key
+can compose requests for future timestamps and use them later.
+
+In release 1.17 and later, freshness tokens are supported by the
+client and are sent by the KDC when the client indicates support for
+them. Because not all clients support freshness tokens yet, they are
+not required by default. To check if freshness tokens are supported
+by a realm's clients, look in the KDC logs for the lines::
+
+ PKINIT: freshness token received from <client principal>
+ PKINIT: no freshness token received from <client principal>
+
+To require freshness tokens for all clients in a realm (except for
+clients authenticating anonymously), set the
+**pkinit_require_freshness** variable to ``true`` in the appropriate
+:ref:`kdc_realms` subsection of the KDC's :ref:`kdc.conf(5)` file. To
+test that this option is in effect, run ``kinit -X disable_freshness``
+and verify that authentication is unsuccessful.
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index e76747102..dba818b26 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -181,6 +181,7 @@ Public
KRB5_KEYUSAGE_KRB_ERROR_CKSUM.rst
KRB5_KEYUSAGE_KRB_PRIV_ENCPART.rst
KRB5_KEYUSAGE_KRB_SAFE_CKSUM.rst
+ KRB5_KEYUSAGE_PA_AS_FRESHNESS.rst
KRB5_KEYUSAGE_PA_FX_COOKIE.rst
KRB5_KEYUSAGE_PA_OTP_REQUEST.rst
KRB5_KEYUSAGE_PA_PKINIT_KX.rst
@@ -241,6 +242,7 @@ Public
KRB5_PADATA_AFS3_SALT.rst
KRB5_PADATA_AP_REQ.rst
KRB5_PADATA_AS_CHECKSUM.rst
+ KRB5_PADATA_AS_FRESHNESS.rst
KRB5_PADATA_ENCRYPTED_CHALLENGE.rst
KRB5_PADATA_ENC_SANDIA_SECURID.rst
KRB5_PADATA_ENC_TIMESTAMP.rst
diff --git a/doc/formats/freshness_token.rst b/doc/formats/freshness_token.rst
new file mode 100644
index 000000000..3127621a9
--- /dev/null
+++ b/doc/formats/freshness_token.rst
@@ -0,0 +1,19 @@
+PKINIT freshness tokens
+=======================
+
+:rfc:`8070` specifies a pa-data type PA_AS_FRESHNESS, which clients
+should reflect within signed PKINIT data to prove recent access to the
+client certificate private key. The contents of a freshness token are
+left to the KDC implementation. The MIT krb5 KDC uses the following
+format for freshness tokens (starting in release 1.17):
+
+* a four-byte big-endian POSIX timestamp
+* a four-byte big-endian key version number
+* an :rfc:`3961` checksum, with no ASN.1 wrapper
+
+The checksum is computed using the first key in the local krbtgt
+principal entry for the realm (e.g. ``krbtgt/KRBTEST.COM@KRBTEST.COM``
+if the request is to the ``KRBTEST.COM`` realm) of the indicated key
+version. The checksum type must be the mandatory checksum type for
+the encryption type of the krbtgt key. The key usage value for the
+checksum is 514.
diff --git a/doc/formats/index.rst b/doc/formats/index.rst
index 8b30626d4..4ad534424 100644
--- a/doc/formats/index.rst
+++ b/doc/formats/index.rst
@@ -7,3 +7,4 @@ Protocols and file formats
ccache_file_format
keytab_file_format
cookie
+ freshness_token
diff --git a/src/include/krb5/kdcpreauth_plugin.h b/src/include/krb5/kdcpreauth_plugin.h
index f38820099..3a4754234 100644
--- a/src/include/krb5/kdcpreauth_plugin.h
+++ b/src/include/krb5/kdcpreauth_plugin.h
@@ -240,6 +240,23 @@ typedef struct krb5_kdcpreauth_callbacks_st {
/* End of version 4 kdcpreauth callbacks. */
+ /*
+ * Instruct the KDC to send a freshness token in the method data
+ * accompanying a PREAUTH_REQUIRED or PREAUTH_FAILED error, if the client
+ * indicated support for freshness tokens. This callback should only be
+ * invoked from the edata method.
+ */
+ void (*send_freshness_token)(krb5_context context,
+ krb5_kdcpreauth_rock rock);
+
+ /* Validate a freshness token sent by the client. Return 0 on success,
+ * KRB5KDC_ERR_PREAUTH_EXPIRED on error. */
+ krb5_error_code (*check_freshness_token)(krb5_context context,
+ krb5_kdcpreauth_rock rock,
+ const krb5_data *token);
+
+ /* End of version 5 kdcpreauth callbacks. */
+
} *krb5_kdcpreauth_callbacks;
/* Optional: preauth plugin initialization function. */
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 833e72335..a650ecece 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -1035,7 +1035,10 @@ krb5_c_keyed_checksum_types(krb5_context context, krb5_enctype enctype,
#define KRB5_KEYUSAGE_AS_REQ 56
#define KRB5_KEYUSAGE_CAMMAC 64
+/* Key usage values 512-1023 are reserved for uses internal to a Kerberos
+ * implementation. */
#define KRB5_KEYUSAGE_PA_FX_COOKIE 513 /**< Used for encrypted FAST cookies */
+#define KRB5_KEYUSAGE_PA_AS_FRESHNESS 514 /**< Used for freshness tokens */
/** @} */ /* end of KRB5_KEYUSAGE group */
/**
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index 7c8da63e1..588c1375a 100644
--- a/src/kdc/do_as_req.c
+++ b/src/kdc/do_as_req.c
@@ -563,6 +563,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
state->rock.rstate = state->rstate;
state->rock.vctx = vctx;
state->rock.auth_indicators = &state->auth_indicators;
+ state->rock.send_freshness_token = FALSE;
if (!state->request->client) {
state->status = "NULL_CLIENT";
errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
@@ -659,6 +660,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
state->status = "GET_LOCAL_TGT";
goto errout;
}
+ state->rock.local_tgt = state->local_tgt;
au_state->stage = VALIDATE_POL;
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 6f34dc289..80b130222 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -87,6 +87,9 @@
#include <assert.h>
#include <krb5/kdcpreauth_plugin.h>
+/* Let freshness tokens be valid for ten minutes. */
+#define FRESHNESS_LIFETIME 600
+
typedef struct preauth_system_st {
const char *name;
int type;
@@ -497,8 +500,68 @@ client_name(krb5_context context, krb5_kdcpreauth_rock rock)
return rock->client->princ;
}
+static void
+send_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock)
+{
+ rock->send_freshness_token = TRUE;
+}
+
+static krb5_error_code
+check_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,
+ const krb5_data *token)
+{
+ krb5_timestamp token_ts, now;
+ krb5_key_data *kd;
+ krb5_keyblock kb;
+ krb5_kvno token_kvno;
+ krb5_checksum cksum;
+ krb5_data d;
+ uint8_t *token_cksum;
+ size_t token_cksum_len;
+ krb5_boolean valid = FALSE;
+ char ckbuf[4];
+
+ memset(&kb, 0, sizeof(kb));
+
+ if (krb5_timeofday(context, &now) != 0)
+ goto cleanup;
+
+ if (token->length <= 8)
+ goto cleanup;
+ token_ts = load_32_be(token->data);
+ token_kvno = load_32_be(token->data + 4);
+ token_cksum = (uint8_t *)token->data + 8;
+ token_cksum_len = token->length - 8;
+
+ /* Check if the token timestamp is too old. */
+ if (ts_after(now, ts_incr(token_ts, FRESHNESS_LIFETIME)))
+ goto cleanup;
+
+ /* Fetch and decrypt the local krbtgt key of the token's kvno. */
+ if (krb5_dbe_find_enctype(context, rock->local_tgt, -1, -1, token_kvno,
+ &kd) != 0)
+ goto cleanup;
+ if (krb5_dbe_decrypt_key_data(context, NULL, kd, &kb, NULL) != 0)
+ goto cleanup;
+
+ /* Verify the token checksum against the current KDC time. The checksum
+ * must use the mandatory checksum type of the krbtgt key's enctype. */
+ store_32_be(token_ts, ckbuf);
+ d = make_data(ckbuf, sizeof(ckbuf));
+ cksum.magic = KV5M_CHECKSUM;
+ cksum.checksum_type = 0;
+ cksum.length = token_cksum_len;
+ cksum.contents = token_cksum;
+ (void)krb5_c_verify_checksum(context, &kb, KRB5_KEYUSAGE_PA_AS_FRESHNESS,
+ &d, &cksum, &valid);
+
+cleanup:
+ krb5_free_keyblock_contents(context, &kb);
+ return valid ? 0 : KRB5KDC_ERR_PREAUTH_EXPIRED;
+}
+
static struct krb5_kdcpreauth_callbacks_st callbacks = {
- 4,
+ 5,
max_time_skew,
client_keys,
free_keys,
@@ -514,7 +577,9 @@ static struct krb5_kdcpreauth_callbacks_st callbacks = {
get_cookie,
set_cookie,
match_client,
- client_name
+ client_name,
+ send_freshness_token,
+ check_freshness_token
};
static krb5_error_code
@@ -770,6 +835,62 @@ cleanup:
return ret;
}
+static krb5_error_code
+add_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,
+ krb5_pa_data ***pa_list)
+{
+ krb5_error_code ret;
+ krb5_timestamp now;
+ krb5_key_data *kd;
+ krb5_keyblock kb;
+ krb5_checksum cksum;
+ krb5_data d;
+ krb5_pa_data *pa;
+ char ckbuf[4];
+
+ memset(&cksum, 0, sizeof(cksum));
+ memset(&kb, 0, sizeof(kb));
+
+ if (!rock->send_freshness_token)
+ return 0;
+ if (krb5int_find_pa_data(context, rock->request->padata,
+ KRB5_PADATA_AS_FRESHNESS) == NULL)
+ return 0;
+
+ /* Fetch and decrypt the current local krbtgt key. */
+ ret = krb5_dbe_find_enctype(context, rock->local_tgt, -1, -1, 0, &kd);
+ if (ret)
+ goto cleanup;
+ ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &kb, NULL);
+ if (ret)
+ goto cleanup;
+
+ /* Compute a checksum over the current KDC time. */
+ ret = krb5_timeofday(context, &now);
+ if (ret)
+ goto cleanup;
+ store_32_be(now, ckbuf);
+ d = make_data(ckbuf, sizeof(ckbuf));
+ ret = krb5_c_make_checksum(context, 0, &kb, KRB5_KEYUSAGE_PA_AS_FRESHNESS,
+ &d, &cksum);
+
+ /* Compose a freshness token from the time, krbtgt kvno, and checksum. */
+ ret = alloc_pa_data(KRB5_PADATA_AS_FRESHNESS, 8 + cksum.length, &pa);
+ if (ret)
+ goto cleanup;
+ store_32_be(now, pa->contents);
+ store_32_be(kd->key_data_kvno, pa->contents + 4);
+ memcpy(pa->contents + 8, cksum.contents, cksum.length);
+
+ /* add_pa_data_element() claims pa on success or failure. */
+ ret = add_pa_data_element(pa_list, pa);
+
+cleanup:
+ krb5_free_keyblock_contents(context, &kb);
+ krb5_free_checksum_contents(context, &cksum);
+ return ret;
+}
+
struct hint_state {
kdc_hint_respond_fn respond;
void *arg;
@@ -792,6 +913,11 @@ hint_list_finish(struct hint_state *state, krb5_error_code code)
void *oldarg = state->arg;
kdc_realm_t *kdc_active_realm = state->realm;
+ /* Add a freshness token if a preauth module requested it and the client
+ * request indicates support for it. */
+ if (!code)
+ code = add_freshness_token(kdc_context, state->rock, &state->pa_data);
+
if (!code) {
if (state->pa_data == NULL) {
krb5_klog_syslog(LOG_INFO,
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index 198eab9c4..1885c9f80 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -426,11 +426,13 @@ struct krb5_kdcpreauth_rock_st {
krb5_kdc_req *request;
krb5_data *inner_body;
krb5_db_entry *client;
+ krb5_db_entry *local_tgt;
krb5_key_data *client_key;
krb5_keyblock *client_keyblock;
struct kdc_request_state *rstate;
verto_ctx *vctx;
krb5_data ***auth_indicators;
+ krb5_boolean send_freshness_token;
};
#define isflagset(flagfield, flag) (flagfield & (flag))
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
index 8489a3e23..fe2ec0d31 100644
--- a/src/plugins/preauth/pkinit/pkinit.h
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -77,6 +77,7 @@
#define KRB5_CONF_PKINIT_KDC_OCSP "pkinit_kdc_ocsp"
#define KRB5_CONF_PKINIT_POOL "pkinit_pool"
#define KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING "pkinit_require_crl_checking"
+#define KRB5_CONF_PKINIT_REQUIRE_FRESHNESS "pkinit_require_freshness"
#define KRB5_CONF_PKINIT_REVOKE "pkinit_revoke"
/* Make pkiDebug(fmt,...) print, or not. */
@@ -148,6 +149,7 @@ typedef struct _pkinit_plg_opts {
int allow_upn; /* allow UPN-SAN instead of pkinit-SAN */
int dh_or_rsa; /* selects DH or RSA based pkinit */
int require_crl_checking; /* require CRL for a CA (default is false) */
+ int require_freshness; /* require freshness token (default is false) */
int disable_freshness; /* disable freshness token on client for testing */
int dh_min_bits; /* minimum DH modulus size allowed */
} pkinit_plg_opts;
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
index 8aa4d8b49..76ad5bf19 100644
--- a/src/plugins/preauth/pkinit/pkinit_srv.c
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -161,6 +161,10 @@ pkinit_server_get_edata(krb5_context context,
if (plgctx == NULL)
retval = EINVAL;
+ /* Send a freshness token if the client requested one. */
+ if (!retval)
+ cb->send_freshness_token(context, rock);
+
(*respond)(arg, retval, NULL);
}
@@ -403,6 +407,31 @@ cleanup:
return ret;
}
+/* Return an error if freshness tokens are required and one was not received.
+ * Log an appropriate message indicating whether a valid token was received. */
+static krb5_error_code
+check_log_freshness(krb5_context context, pkinit_kdc_context plgctx,
+ krb5_kdc_req *request, krb5_boolean valid_freshness_token)
+{
+ krb5_error_code ret;
+ char *name = NULL;
+
+ ret = krb5_unparse_name(context, request->client, &name);
+ if (ret)
+ return ret;
+ if (plgctx->opts->require_freshness && !valid_freshness_token) {
+ com_err("", 0, _("PKINIT: no freshness token, rejecting auth from %s"),
+ name);
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ } else if (valid_freshness_token) {
+ com_err("", 0, _("PKINIT: freshness token received from %s"), name);
+ } else {
+ com_err("", 0, _("PKINIT: no freshness token received from %s"), name);
+ }
+ krb5_free_unparsed_name(context, name);
+ return ret;
+}
+
static void
pkinit_server_verify_padata(krb5_context context,
krb5_data *req_pkt,
@@ -425,10 +454,11 @@ pkinit_server_verify_padata(krb5_context context,
pkinit_kdc_req_context reqctx = NULL;
krb5_checksum cksum = {0, 0, 0, NULL};
krb5_data *der_req = NULL;
- krb5_data k5data;
+ krb5_data k5data, *ftoken;
int is_signed = 1;
krb5_pa_data **e_data = NULL;
krb5_kdcpreauth_modreq modreq = NULL;
+ krb5_boolean valid_freshness_token = FALSE;
char **sp;
pkiDebug("pkinit_verify_padata: entered!\n");
@@ -599,6 +629,14 @@ pkinit_server_verify_padata(krb5_context context,
goto cleanup;
}
+ ftoken = auth_pack->pkAuthenticator.freshnessToken;
+ if (ftoken != NULL) {
+ retval = cb->check_freshness_token(context, rock, ftoken);
+ if (retval)
+ goto cleanup;
+ valid_freshness_token = TRUE;
+ }
+
/* check if kdcPkId present and match KDC's subjectIdentifier */
if (reqp->kdcPkId.data != NULL) {
int valid_kdcPkId = 0;
@@ -641,6 +679,13 @@ pkinit_server_verify_padata(krb5_context context,
break;
}
+ if (is_signed) {
+ retval = check_log_freshness(context, plgctx, request,
+ valid_freshness_token);
+ if (retval)
+ goto cleanup;
+ }
+
if (is_signed && plgctx->auth_indicators != NULL) {
/* Assert configured authentication indicators. */
for (sp = plgctx->auth_indicators; *sp != NULL; sp++) {
@@ -1330,6 +1375,10 @@ pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx)
KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING,
0, &plgctx->opts->require_crl_checking);
+ pkinit_kdcdefault_boolean(context, plgctx->realmname,
+ KRB5_CONF_PKINIT_REQUIRE_FRESHNESS,
+ 0, &plgctx->opts->require_freshness);
+
pkinit_kdcdefault_string(context, plgctx->realmname,
KRB5_CONF_PKINIT_EKU_CHECKING,
&eku_string);
diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py
index 86fe661a0..5bc60cb1e 100755
--- a/src/tests/t_pkinit.py
+++ b/src/tests/t_pkinit.py
@@ -39,6 +39,8 @@ pkinit_kdc_conf = {'realms': {'$realm': {
'pkinit_indicator': ['indpkinit1', 'indpkinit2']}}}
restrictive_kdc_conf = {'realms': {'$realm': {
'restrict_anonymous_to_tgt': 'true' }}}
+freshness_kdc_conf = {'realms': {'$realm': {
+ 'pkinit_require_freshness': 'true'}}}
testprincs = {'krbtgt/KRBTEST.COM': {'keys': 'aes128-cts'},
'user': {'keys': 'aes128-cts', 'flags': '+preauth'},
@@ -118,6 +120,10 @@ realm.kinit(realm.user_princ, password=password('user'))
realm.klist(realm.user_princ)
realm.run([kvno, realm.host_princ])
+# Having tested password preauth, remove the keys for better error
+# reporting.
+realm.run([kadminl, 'purgekeys', '-all', realm.user_princ])
+
# Test anonymous PKINIT.
realm.kinit('@%s' % realm.realm, flags=['-n'], expected_code=1,
expected_msg='not found in Kerberos database')
@@ -153,23 +159,32 @@ realm.run([kvno, realm.host_princ], expected_code=1,
realm.kinit(realm.host_princ, flags=['-k'])
realm.run([kvno, '-U', 'user', realm.host_princ])
-# Go back to a normal KDC and disable anonymous PKINIT.
+# Go back to the normal KDC environment.
realm.stop_kdc()
realm.start_kdc()
-realm.run([kadminl, 'delprinc', 'WELLKNOWN/ANONYMOUS'])
# Run the basic test - PKINIT with FILE: identity, with no password on the key.
-realm.run(['./responder', '-x', 'pkinit=',
- '-X', 'X509_user_identity=%s' % file_identity, realm.user_princ])
realm.kinit(realm.user_princ,
- flags=['-X', 'X509_user_identity=%s' % file_identity])
+ flags=['-X', 'X509_user_identity=%s' % file_identity],
+ expected_trace=('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'PKINIT client received freshness token from KDC',
+ 'PKINIT loading CA certs and CRLs from FILE',
+ 'PKINIT client making DH request',
+ 'Produced preauth for next request: 133, 16',
+ 'PKINIT client verified DH reply',
+ 'PKINIT client found id-pkinit-san in KDC cert',
+ 'PKINIT client matched KDC principal krbtgt/'))
realm.klist(realm.user_princ)
realm.run([kvno, realm.host_princ])
# Try again using RSA instead of DH.
realm.kinit(realm.user_princ,
flags=['-X', 'X509_user_identity=%s' % file_identity,
- '-X', 'flag_RSA_PROTOCOL=yes'])
+ '-X', 'flag_RSA_PROTOCOL=yes'],
+ expected_trace=('PKINIT client making RSA request',
+ 'PKINIT client verified RSA reply'))
realm.klist(realm.user_princ)
# Test a DH parameter renegotiation by temporarily setting a 4096-bit
@@ -192,8 +207,23 @@ expected_trace = ('Sending unauthenticated request',
realm.kinit(realm.user_princ,
flags=['-X', 'X509_user_identity=%s' % file_identity],
expected_trace=expected_trace)
+
+# Test enforcement of required freshness tokens. (We can leave
+# freshness tokens required after this test.)
+realm.kinit(realm.user_princ,
+ flags=['-X', 'X509_user_identity=%s' % file_identity,
+ '-X', 'disable_freshness=yes'])
+f_env = realm.special_env('freshness', True, kdc_conf=freshness_kdc_conf)
realm.stop_kdc()
-realm.start_kdc()
+realm.start_kdc(env=f_env)
+realm.kinit(realm.user_princ,
+ flags=['-X', 'X509_user_identity=%s' % file_identity])
+realm.kinit(realm.user_princ,
+ flags=['-X', 'X509_user_identity=%s' % file_identity,
+ '-X', 'disable_freshness=yes'],
+ expected_code=1, expected_msg='Preauthentication failed')
+# Anonymous should never require a freshness token.
+realm.kinit('@%s' % realm.realm, flags=['-n', '-X', 'disable_freshness=yes'])
# Run the basic test - PKINIT with FILE: identity, with a password on the key,
# supplied by the prompter.
@@ -229,8 +259,6 @@ shutil.copy(privkey_pem, os.path.join(path, 'user.key'))
shutil.copy(privkey_enc_pem, os.path.join(path_enc, 'user.key'))
shutil.copy(user_pem, os.path.join(path, 'user.crt'))
shutil.copy(user_pem, os.path.join(path_enc, 'user.crt'))
-realm.run(['./responder', '-x', 'pkinit=', '-X',
- 'X509_user_identity=%s' % dir_identity, realm.user_princ])
realm.kinit(realm.user_princ,
flags=['-X', 'X509_user_identity=%s' % dir_identity])
realm.klist(realm.user_princ)
@@ -262,8 +290,6 @@ realm.klist(realm.user_princ)
realm.run([kvno, realm.host_princ])
# PKINIT with PKCS12: identity, with no password on the bundle.
-realm.run(['./responder', '-x', 'pkinit=',
- '-X', 'X509_user_identity=%s' % p12_identity, realm.user_princ])
realm.kinit(realm.user_princ,
flags=['-X', 'X509_user_identity=%s' % p12_identity])
realm.klist(realm.user_princ)
@@ -357,8 +383,6 @@ conf = open(softpkcs11rc, 'w')
conf.write("%s\t%s\t%s\t%s\n" % ('user', 'user token', user_pem, privkey_pem))
conf.close()
# Expect to succeed without having to supply any more information.
-realm.run(['./responder', '-x', 'pkinit=',
- '-X', 'X509_user_identity=%s' % p11_identity, realm.user_princ])
realm.kinit(realm.user_princ,
flags=['-X', 'X509_user_identity=%s' % p11_identity])
realm.klist(realm.user_princ)

View File

@ -1,336 +0,0 @@
From 5edc6de93196b4f07da6695a4b271a067000c84d Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 31 Jan 2017 17:02:34 -0500
Subject: [PATCH] Add PKINIT client support for freshness token
Send an empty PA_AS_FRESHNESS padata item in unauthenticated AS
requests to indicate support for RFC 8070. If the KDC includes a
PA_AS_FRESHNESS value in its method data, echo it back in the new
freshnessToken field of pkAuthenticator
ticket: 8648
(cherry picked from commit 085785362e01467cb25c79a90dcebfba9ea019d8)
---
doc/user/user_commands/kinit.rst | 3 +++
src/include/k5-int-pkinit.h | 1 +
src/include/krb5/krb5.hin | 1 +
src/lib/krb5/asn.1/asn1_k_encode.c | 5 ++++-
src/lib/krb5/krb/get_in_tkt.c | 12 ++++++++----
src/lib/krb5/krb/init_creds_ctx.h | 2 +-
src/plugins/preauth/pkinit/pkinit.h | 3 +++
src/plugins/preauth/pkinit/pkinit_clnt.c | 19 ++++++++++++++++++-
src/plugins/preauth/pkinit/pkinit_lib.c | 3 +++
src/plugins/preauth/pkinit/pkinit_trace.h | 2 ++
src/tests/asn.1/ktest.c | 4 ++++
src/tests/asn.1/pkinit_encode.out | 2 +-
src/tests/asn.1/pkinit_trval.out | 1 +
13 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/doc/user/user_commands/kinit.rst b/doc/user/user_commands/kinit.rst
index 3f9d5340f..1f696920f 100644
--- a/doc/user/user_commands/kinit.rst
+++ b/doc/user/user_commands/kinit.rst
@@ -197,6 +197,9 @@ OPTIONS
specify use of RSA, rather than the default Diffie-Hellman
protocol
+ **disable_freshness**\ [**=yes**]
+ disable sending freshness tokens (for testing purposes only)
+
ENVIRONMENT
-----------
diff --git a/src/include/k5-int-pkinit.h b/src/include/k5-int-pkinit.h
index 7b2f595cb..4622a629e 100644
--- a/src/include/k5-int-pkinit.h
+++ b/src/include/k5-int-pkinit.h
@@ -42,6 +42,7 @@ typedef struct _krb5_pk_authenticator {
krb5_timestamp ctime;
krb5_int32 nonce; /* (0..4294967295) */
krb5_checksum paChecksum;
+ krb5_data *freshnessToken;
} krb5_pk_authenticator;
/* PKAuthenticator draft9 */
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index e81bb0a6d..833e72335 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -1879,6 +1879,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
#define KRB5_PADATA_OTP_PIN_CHANGE 144 /**< RFC 6560 section 4.3 */
#define KRB5_PADATA_PKINIT_KX 147 /**< RFC 6112 */
#define KRB5_ENCPADATA_REQ_ENC_PA_REP 149 /**< RFC 6806 */
+#define KRB5_PADATA_AS_FRESHNESS 150 /**< RFC 8070 */
#define KRB5_SAM_USE_SAD_AS_KEY 0x80000000
#define KRB5_SAM_SEND_ENCRYPTED_SAD 0x40000000
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index 889460989..3b23fe34a 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -1442,9 +1442,12 @@ DEFFIELD(pk_authenticator_1, krb5_pk_authenticator, ctime, 1, kerberos_time);
DEFFIELD(pk_authenticator_2, krb5_pk_authenticator, nonce, 2, int32);
DEFFIELD(pk_authenticator_3, krb5_pk_authenticator, paChecksum, 3,
ostring_checksum);
+DEFFIELD(pk_authenticator_4, krb5_pk_authenticator, freshnessToken, 4,
+ opt_ostring_data_ptr);
static const struct atype_info *pk_authenticator_fields[] = {
&k5_atype_pk_authenticator_0, &k5_atype_pk_authenticator_1,
- &k5_atype_pk_authenticator_2, &k5_atype_pk_authenticator_3
+ &k5_atype_pk_authenticator_2, &k5_atype_pk_authenticator_3,
+ &k5_atype_pk_authenticator_4
};
DEFSEQTYPE(pk_authenticator, krb5_pk_authenticator, pk_authenticator_fields);
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 47a00bf2c..1d96ff163 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -895,7 +895,7 @@ krb5_init_creds_init(krb5_context context,
ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
if (code != 0)
goto cleanup;
- ctx->enc_pa_rep_permitted = TRUE;
+ ctx->info_pa_permitted = TRUE;
code = krb5_copy_principal(context, client, &ctx->request->client);
if (code != 0)
goto cleanup;
@@ -1389,7 +1389,11 @@ init_creds_step_request(krb5_context context,
krb5_free_data(context, ctx->encoded_previous_request);
ctx->encoded_previous_request = NULL;
}
- if (ctx->enc_pa_rep_permitted) {
+ if (ctx->info_pa_permitted) {
+ code = add_padata(&ctx->request->padata, KRB5_PADATA_AS_FRESHNESS,
+ NULL, 0);
+ if (code)
+ goto cleanup;
code = add_padata(&ctx->request->padata, KRB5_ENCPADATA_REQ_ENC_PA_REP,
NULL, 0);
}
@@ -1530,7 +1534,7 @@ init_creds_step_reply(krb5_context context,
ctx->selected_preauth_type == KRB5_PADATA_NONE) {
/* The KDC didn't like our informational padata (probably a pre-1.7
* MIT krb5 KDC). Retry without it. */
- ctx->enc_pa_rep_permitted = FALSE;
+ ctx->info_pa_permitted = FALSE;
ctx->restarted = TRUE;
code = restart_init_creds_loop(context, ctx, FALSE);
} else if (reply_code == KDC_ERR_PREAUTH_EXPIRED) {
@@ -1574,7 +1578,7 @@ init_creds_step_reply(krb5_context context,
goto cleanup;
/* Reset per-realm negotiation state. */
ctx->restarted = FALSE;
- ctx->enc_pa_rep_permitted = TRUE;
+ ctx->info_pa_permitted = TRUE;
code = restart_init_creds_loop(context, ctx, FALSE);
} else {
if (retry && ctx->selected_preauth_type != KRB5_PADATA_NONE) {
diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
index fe769685b..b19410a13 100644
--- a/src/lib/krb5/krb/init_creds_ctx.h
+++ b/src/lib/krb5/krb/init_creds_ctx.h
@@ -58,7 +58,7 @@ struct _krb5_init_creds_context {
krb5_data s2kparams;
krb5_keyblock as_key;
krb5_enctype etype;
- krb5_boolean enc_pa_rep_permitted;
+ krb5_boolean info_pa_permitted;
krb5_boolean restarted;
struct krb5_responder_context_st rctx;
krb5_preauthtype selected_preauth_type;
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
index f3de9ad7a..8489a3e23 100644
--- a/src/plugins/preauth/pkinit/pkinit.h
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -148,6 +148,7 @@ typedef struct _pkinit_plg_opts {
int allow_upn; /* allow UPN-SAN instead of pkinit-SAN */
int dh_or_rsa; /* selects DH or RSA based pkinit */
int require_crl_checking; /* require CRL for a CA (default is false) */
+ int disable_freshness; /* disable freshness token on client for testing */
int dh_min_bits; /* minimum DH modulus size allowed */
} pkinit_plg_opts;
@@ -162,6 +163,7 @@ typedef struct _pkinit_req_opts {
int require_crl_checking;
int dh_size; /* initial request DH modulus size (default=1024) */
int require_hostname_match;
+ int disable_freshness;
} pkinit_req_opts;
/*
@@ -214,6 +216,7 @@ struct _pkinit_req_context {
int identity_initialized;
int identity_prompted;
krb5_error_code identity_prompt_retval;
+ krb5_data *freshness_token;
};
typedef struct _pkinit_req_context *pkinit_req_context;
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index f1bc6b21d..9483d69e5 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -231,6 +231,8 @@ pkinit_as_req_create(krb5_context context,
auth_pack.pkAuthenticator.cusec = cusec;
auth_pack.pkAuthenticator.nonce = nonce;
auth_pack.pkAuthenticator.paChecksum = *cksum;
+ if (!reqctx->opts->disable_freshness)
+ auth_pack.pkAuthenticator.freshnessToken = reqctx->freshness_token;
auth_pack.clientDHNonce.length = 0;
auth_pack.clientPublicValue = &info;
auth_pack.supportedKDFs = (krb5_data **)supported_kdf_alg_ids;
@@ -1162,6 +1164,7 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
pkinit_context plgctx = (pkinit_context)moddata;
pkinit_req_context reqctx = (pkinit_req_context)modreq;
krb5_keyblock as_key;
+ krb5_data d;
pkiDebug("pkinit_client_process %p %p %p %p\n",
context, plgctx, reqctx, request);
@@ -1174,6 +1177,12 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
case KRB5_PADATA_PKINIT_KX:
reqctx->rfc6112_kdc = 1;
return 0;
+ case KRB5_PADATA_AS_FRESHNESS:
+ TRACE_PKINIT_CLIENT_FRESHNESS_TOKEN(context);
+ krb5_free_data(context, reqctx->freshness_token);
+ reqctx->freshness_token = NULL;
+ d = make_data(in_padata->contents, in_padata->length);
+ return krb5_copy_data(context, &d, &reqctx->freshness_token);
case KRB5_PADATA_PK_AS_REQ:
reqctx->rfc4556_kdc = 1;
pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
@@ -1359,7 +1368,7 @@ cleanup:
static int
pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
{
- if (patype == KRB5_PADATA_PKINIT_KX)
+ if (patype == KRB5_PADATA_PKINIT_KX || patype == KRB5_PADATA_AS_FRESHNESS)
return PA_INFO;
return PA_REAL;
}
@@ -1376,6 +1385,7 @@ static krb5_preauthtype supported_client_pa_types[] = {
KRB5_PADATA_PK_AS_REP_OLD,
KRB5_PADATA_PK_AS_REQ_OLD,
KRB5_PADATA_PKINIT_KX,
+ KRB5_PADATA_AS_FRESHNESS,
0
};
@@ -1400,6 +1410,7 @@ pkinit_client_req_init(krb5_context context,
reqctx->opts = NULL;
reqctx->idctx = NULL;
reqctx->idopts = NULL;
+ reqctx->freshness_token = NULL;
retval = pkinit_init_req_opts(&reqctx->opts);
if (retval)
@@ -1410,6 +1421,7 @@ pkinit_client_req_init(krb5_context context,
reqctx->opts->dh_or_rsa = plgctx->opts->dh_or_rsa;
reqctx->opts->allow_upn = plgctx->opts->allow_upn;
reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking;
+ reqctx->opts->disable_freshness = plgctx->opts->disable_freshness;
retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
if (retval)
@@ -1468,6 +1480,8 @@ pkinit_client_req_fini(krb5_context context, krb5_clpreauth_moddata moddata,
if (reqctx->idopts != NULL)
pkinit_fini_identity_opts(reqctx->idopts);
+ krb5_free_data(context, reqctx->freshness_token);
+
free(reqctx);
return;
}
@@ -1580,6 +1594,9 @@ handle_gic_opt(krb5_context context,
pkiDebug("Setting flag to use RSA_PROTOCOL\n");
plgctx->opts->dh_or_rsa = RSA_PROTOCOL;
}
+ } else if (strcmp(attr, "disable_freshness") == 0) {
+ if (strcmp(value, "yes") == 0)
+ plgctx->opts->disable_freshness = 1;
}
return 0;
}
diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c
index 2f88545da..d5858c424 100644
--- a/src/plugins/preauth/pkinit/pkinit_lib.c
+++ b/src/plugins/preauth/pkinit/pkinit_lib.c
@@ -82,6 +82,8 @@ pkinit_init_plg_opts(pkinit_plg_opts **plgopts)
opts->dh_or_rsa = DH_PROTOCOL;
opts->allow_upn = 0;
opts->require_crl_checking = 0;
+ opts->require_freshness = 0;
+ opts->disable_freshness = 0;
opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
@@ -145,6 +147,7 @@ free_krb5_auth_pack(krb5_auth_pack **in)
free((*in)->clientPublicValue);
}
free((*in)->pkAuthenticator.paChecksum.contents);
+ krb5_free_data(NULL, (*in)->pkAuthenticator.freshnessToken);
if ((*in)->supportedCMSTypes != NULL)
free_krb5_algorithm_identifiers(&((*in)->supportedCMSTypes));
if ((*in)->supportedKDFs) {
diff --git a/src/plugins/preauth/pkinit/pkinit_trace.h b/src/plugins/preauth/pkinit/pkinit_trace.h
index 2d95da94a..7f95206c0 100644
--- a/src/plugins/preauth/pkinit/pkinit_trace.h
+++ b/src/plugins/preauth/pkinit/pkinit_trace.h
@@ -41,6 +41,8 @@
TRACE(c, "PKINIT client found no acceptable EKU in KDC cert")
#define TRACE_PKINIT_CLIENT_EKU_SKIP(c) \
TRACE(c, "PKINIT client skipping EKU check due to configuration")
+#define TRACE_PKINIT_CLIENT_FRESHNESS_TOKEN(c) \
+ TRACE(c, "PKINIT client received freshness token from KDC")
#define TRACE_PKINIT_CLIENT_KDF_ALG(c, kdf, keyblock) \
TRACE(c, "PKINIT client used KDF {hexdata} to compute reply key " \
"{keyblock}", kdf, keyblock)
diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c
index 43084cbbd..cf63f3f66 100644
--- a/src/tests/asn.1/ktest.c
+++ b/src/tests/asn.1/ktest.c
@@ -725,6 +725,8 @@ ktest_make_sample_pk_authenticator(krb5_pk_authenticator *p)
ktest_make_sample_checksum(&p->paChecksum);
/* We don't encode the checksum type, only the contents. */
p->paChecksum.checksum_type = 0;
+ p->freshnessToken = ealloc(sizeof(krb5_data));
+ ktest_make_sample_data(p->freshnessToken);
}
static void
@@ -1651,6 +1653,8 @@ ktest_empty_pk_authenticator(krb5_pk_authenticator *p)
{
ktest_empty_checksum(&p->paChecksum);
p->paChecksum.contents = NULL;
+ krb5_free_data(NULL, p->freshnessToken);
+ p->freshnessToken = NULL;
}
static void
diff --git a/src/tests/asn.1/pkinit_encode.out b/src/tests/asn.1/pkinit_encode.out
index 463128de0..3b0f7190a 100644
--- a/src/tests/asn.1/pkinit_encode.out
+++ b/src/tests/asn.1/pkinit_encode.out
@@ -4,7 +4,7 @@ encode_krb5_pa_pk_as_rep(dhInfo): A0 28 30 26 80 08 6B 72 62 35 64 61 74 61 A1 0
encode_krb5_pa_pk_as_rep(encKeyPack): 81 08 6B 72 62 35 64 61 74 61
encode_krb5_pa_pk_as_rep_draft9(dhSignedData): 80 08 6B 72 62 35 64 61 74 61
encode_krb5_pa_pk_as_rep_draft9(encKeyPack): 81 08 6B 72 62 35 64 61 74 61
-encode_krb5_auth_pack: 30 81 93 A0 29 30 27 A0 05 02 03 01 E2 40 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 03 02 01 2A A3 06 04 04 31 32 33 34 A1 22 30 20 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 03 09 00 6B 72 62 35 64 61 74 61 A2 24 30 22 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 30 0B 06 09 2A 86 48 86 F7 12 01 02 02 A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 10 30 0E 30 0C A0 0A 06 08 6B 72 62 35 64 61 74 61
+encode_krb5_auth_pack: 30 81 9F A0 35 30 33 A0 05 02 03 01 E2 40 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 03 02 01 2A A3 06 04 04 31 32 33 34 A4 0A 04 08 6B 72 62 35 64 61 74 61 A1 22 30 20 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 03 09 00 6B 72 62 35 64 61 74 61 A2 24 30 22 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 30 0B 06 09 2A 86 48 86 F7 12 01 02 02 A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 10 30 0E 30 0C A0 0A 06 08 6B 72 62 35 64 61 74 61
encode_krb5_auth_pack_draft9: 30 75 A0 4F 30 4D A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 05 02 03 01 E2 40 A3 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A4 03 02 01 2A A1 22 30 20 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 03 09 00 6B 72 62 35 64 61 74 61
encode_krb5_kdc_dh_key_info: 30 25 A0 0B 03 09 00 6B 72 62 35 64 61 74 61 A1 03 02 01 2A A2 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A
encode_krb5_reply_key_pack: 30 26 A0 13 30 11 A0 03 02 01 01 A1 0A 04 08 31 32 33 34 35 36 37 38 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34
diff --git a/src/tests/asn.1/pkinit_trval.out b/src/tests/asn.1/pkinit_trval.out
index 58d870631..f9edbe154 100644
--- a/src/tests/asn.1/pkinit_trval.out
+++ b/src/tests/asn.1/pkinit_trval.out
@@ -57,6 +57,7 @@ encode_krb5_auth_pack:
. . [1] [Generalized Time] "19940610060317Z"
. . [2] [Integer] 42
. . [3] [Octet String] "1234"
+. . [4] [Octet String] "krb5data"
. [1] [Sequence/Sequence Of]
. . [Sequence/Sequence Of]
. . . [Object Identifier] <9>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,169 @@
From 5c2f409c360560c8b99926d6cf1a80419e758b22 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 7 Mar 2023 00:19:33 -0500
Subject: [PATCH] Add a simple DER support header
(cherry picked from commit 548da160b52b25a106e9f6077d6a42c2c049586c)
---
src/include/k5-der.h | 149 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 149 insertions(+)
create mode 100644 src/include/k5-der.h
diff --git a/src/include/k5-der.h b/src/include/k5-der.h
new file mode 100644
index 0000000000..b8371d9b4d
--- /dev/null
+++ b/src/include/k5-der.h
@@ -0,0 +1,149 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-der.h - Distinguished Encoding Rules (DER) declarations */
+/*
+ * Copyright (C) 2023 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Most ASN.1 encoding and decoding is done using the table-driven framework in
+ * libkrb5. When that is not an option, these helpers can be used to encode
+ * and decode simple types.
+ */
+
+#ifndef K5_DER_H
+#define K5_DER_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "k5-buf.h"
+#include "k5-input.h"
+
+/* Return the number of bytes needed to encode len as a DER encoding length. */
+static inline size_t
+k5_der_len_len(size_t len)
+{
+ size_t llen;
+
+ if (len < 128)
+ return 1;
+ llen = 1;
+ while (len > 0) {
+ len >>= 8;
+ llen++;
+ }
+ return llen;
+}
+
+/* Return the number of bytes needed to encode a DER value (with identifier
+ * byte and length) for a given contents length. */
+static inline size_t
+k5_der_value_len(size_t contents_len)
+{
+ return 1 + k5_der_len_len(contents_len) + contents_len;
+}
+
+/* Add a DER identifier byte (composed by the caller, including the ASN.1
+ * class, tag, and constructed bit) and length. */
+static inline void
+k5_der_add_taglen(struct k5buf *buf, uint8_t idbyte, size_t len)
+{
+ uint8_t *p;
+ size_t llen = k5_der_len_len(len);
+
+ p = k5_buf_get_space(buf, 1 + llen);
+ if (p == NULL)
+ return;
+ *p++ = idbyte;
+ if (len < 128) {
+ *p = len;
+ } else {
+ *p = 0x80 | (llen - 1);
+ /* Encode the length bytes backwards so the most significant byte is
+ * first. */
+ p += llen;
+ while (len > 0) {
+ *--p = len & 0xFF;
+ len >>= 8;
+ }
+ }
+}
+
+/* Add a DER value (identifier byte, length, and contents). */
+static inline void
+k5_der_add_value(struct k5buf *buf, uint8_t idbyte, const void *contents,
+ size_t len)
+{
+ k5_der_add_taglen(buf, idbyte, len);
+ k5_buf_add_len(buf, contents, len);
+}
+
+/*
+ * If the next byte in in matches idbyte and the subsequent DER length is
+ * valid, advance in past the value, set *contents_out to the value contents,
+ * and return true. Otherwise return false. Only set an error on in if the
+ * next bytes matches idbyte but the ensuing length is invalid. contents_out
+ * may be aliased to in; it will only be written to on successful decoding of a
+ * value.
+ */
+static inline bool
+k5_der_get_value(struct k5input *in, uint8_t idbyte,
+ struct k5input *contents_out)
+{
+ uint8_t lenbyte, i;
+ size_t len;
+ const void *bytes;
+
+ /* Do nothing if in is empty or the next byte doesn't match idbyte. */
+ if (in->status || in->len == 0 || *in->ptr != idbyte)
+ return false;
+
+ /* Advance past the identifier byte and decode the length. */
+ (void)k5_input_get_byte(in);
+ lenbyte = k5_input_get_byte(in);
+ if (lenbyte < 128) {
+ len = lenbyte;
+ } else {
+ len = 0;
+ for (i = 0; i < (lenbyte & 0x7F); i++) {
+ if (len > (SIZE_MAX >> 8)) {
+ k5_input_set_status(in, EOVERFLOW);
+ return false;
+ }
+ len = (len << 8) | k5_input_get_byte(in);
+ }
+ }
+
+ bytes = k5_input_get_bytes(in, len);
+ if (bytes == NULL)
+ return false;
+ k5_input_init(contents_out, bytes, len);
+ return true;
+}
+
+#endif /* K5_DER_H */
--
2.45.1

View File

@ -0,0 +1,420 @@
From 8182f9f08b2593ff8749078ffd3daef9bf39a7fe Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Fri, 20 Mar 2020 00:17:28 +0100
Subject: [PATCH] Add channel bindings tests
[ghudson@mit.edu: adjusted test program to output channel-bound state
instead of optionally enforcing it; adjusted tests to check program
output; split out tests into separate Python script; made cosmetic
changes]
ticket: 8900
(cherry picked from commit b0b21b6d25b06f3e2b365dfe9dd4c99b3d43bf57)
[rharwood@redhat.com: .gitignore]
(cherry picked from commit 3e92520c1417f22447751cd9172d5ab30c2e0ad8)
---
src/plugins/gssapi/negoextest/main.c | 18 +++++
src/tests/gssapi/Makefile.in | 49 ++++++------
src/tests/gssapi/common.c | 25 ++++--
src/tests/gssapi/common.h | 9 +++
src/tests/gssapi/deps | 4 +
src/tests/gssapi/t_bindings.c | 111 +++++++++++++++++++++++++++
src/tests/gssapi/t_bindings.py | 43 +++++++++++
src/tests/gssapi/t_negoex.py | 7 ++
8 files changed, 237 insertions(+), 29 deletions(-)
create mode 100644 src/tests/gssapi/t_bindings.c
create mode 100644 src/tests/gssapi/t_bindings.py
diff --git a/src/plugins/gssapi/negoextest/main.c b/src/plugins/gssapi/negoextest/main.c
index 6c340f41b..72fc5273a 100644
--- a/src/plugins/gssapi/negoextest/main.c
+++ b/src/plugins/gssapi/negoextest/main.c
@@ -57,6 +57,15 @@ gss_init_sec_context(OM_uint32 *minor_status,
const char *envstr;
uint8_t hops, mech_last_octet;
+ envstr = getenv("GSS_INIT_BINDING");
+ if (envstr != NULL) {
+ assert(strlen(envstr) > 0);
+ assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
+ assert(strlen(envstr) == input_chan_bindings->application_data.length);
+ assert(strcmp((char *)input_chan_bindings->application_data.value,
+ envstr) == 0);
+ }
+
if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
envstr = getenv("HOPS");
hops = (envstr != NULL) ? atoi(envstr) : 1;
@@ -112,6 +121,15 @@ gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
uint8_t hops, mech_last_octet;
const char *envstr;
+ envstr = getenv("GSS_ACCEPT_BINDING");
+ if (envstr != NULL) {
+ assert(strlen(envstr) > 0);
+ assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
+ assert(strlen(envstr) == input_chan_bindings->application_data.length);
+ assert(strcmp((char *)input_chan_bindings->application_data.value,
+ envstr) == 0);
+ }
+
/*
* The unwrapped token sits at the end and is just one byte giving the
* remaining number of hops. The final octet of the mech encoding should
diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in
index 5cc1e0f58..68c132b79 100644
--- a/src/tests/gssapi/Makefile.in
+++ b/src/tests/gssapi/Makefile.in
@@ -9,33 +9,33 @@ LOCALINCLUDES = -I$(srcdir)/../../lib/gssapi/mechglue \
-I../../lib/gssapi/generic
SRCS= $(srcdir)/ccinit.c $(srcdir)/ccrefresh.c $(srcdir)/common.c \
- $(srcdir)/t_accname.c $(srcdir)/t_add_cred.c $(srcdir)/t_ccselect.c \
- $(srcdir)/t_ciflags.c $(srcdir)/t_context.c $(srcdir)/t_credstore.c \
- $(srcdir)/t_enctypes.c $(srcdir)/t_err.c $(srcdir)/t_export_cred.c \
- $(srcdir)/t_export_name.c $(srcdir)/t_gssexts.c \
- $(srcdir)/t_imp_cred.c $(srcdir)/t_imp_name.c $(srcdir)/t_invalid.c \
- $(srcdir)/t_inq_cred.c $(srcdir)/t_inq_ctx.c \
+ $(srcdir)/t_accname.c $(srcdir)/t_add_cred.c $(srcdir)/t_bindings.c \
+ $(srcdir)/t_ccselect.c $(srcdir)/t_ciflags.c $(srcdir)/t_context.c \
+ $(srcdir)/t_credstore.c $(srcdir)/t_enctypes.c $(srcdir)/t_err.c \
+ $(srcdir)/t_export_cred.c $(srcdir)/t_export_name.c \
+ $(srcdir)/t_gssexts.c $(srcdir)/t_imp_cred.c $(srcdir)/t_imp_name.c \
+ $(srcdir)/t_invalid.c $(srcdir)/t_inq_cred.c $(srcdir)/t_inq_ctx.c \
$(srcdir)/t_inq_mechs_name.c $(srcdir)/t_iov.c \
$(srcdir)/t_lifetime.c $(srcdir)/t_namingexts.c $(srcdir)/t_oid.c \
$(srcdir)/t_pcontok.c $(srcdir)/t_prf.c $(srcdir)/t_s4u.c \
$(srcdir)/t_s4u2proxy_krb5.c $(srcdir)/t_saslname.c \
$(srcdir)/t_spnego.c $(srcdir)/t_srcattrs.c
-OBJS= ccinit.o ccrefresh.o common.o t_accname.o t_add_cred.o t_ccselect.o \
- t_ciflags.o t_context.o t_credstore.o t_enctypes.o t_err.o \
- t_export_cred.o t_export_name.o t_gssexts.o t_imp_cred.o t_imp_name.o \
- t_invalid.o t_inq_cred.o t_inq_ctx.o t_inq_mechs_name.o t_iov.o \
- t_lifetime.o t_namingexts.o t_oid.o t_pcontok.o t_prf.o t_s4u.o \
- t_s4u2proxy_krb5.o t_saslname.o t_spnego.o t_srcattrs.o
+OBJS= ccinit.o ccrefresh.o common.o t_accname.o t_add_cred.o t_bindings.o \
+ t_ccselect.o t_ciflags.o t_context.o t_credstore.o t_enctypes.o \
+ t_err.o t_export_cred.o t_export_name.o t_gssexts.o t_imp_cred.o \
+ t_imp_name.o t_invalid.o t_inq_cred.o t_inq_ctx.o t_inq_mechs_name.o \
+ t_iov.o t_lifetime.o t_namingexts.o t_oid.o t_pcontok.o t_prf.o \
+ t_s4u.o t_s4u2proxy_krb5.o t_saslname.o t_spnego.o t_srcattrs.o
COMMON_DEPS= common.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
COMMON_LIBS= common.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
-all: ccinit ccrefresh t_accname t_add_cred t_ccselect t_ciflags t_context \
- t_credstore t_enctypes t_err t_export_cred t_export_name t_gssexts \
- t_imp_cred t_imp_name t_invalid t_inq_cred t_inq_ctx t_inq_mechs_name \
- t_iov t_lifetime t_namingexts t_oid t_pcontok t_prf t_s4u \
- t_s4u2proxy_krb5 t_saslname t_spnego t_srcattrs
+all: ccinit ccrefresh t_accname t_add_cred t_bindings t_ccselect t_ciflags \
+ t_context t_credstore t_enctypes t_err t_export_cred t_export_name \
+ t_gssexts t_imp_cred t_imp_name t_invalid t_inq_cred t_inq_ctx \
+ t_inq_mechs_name t_iov t_lifetime t_namingexts t_oid t_pcontok t_prf \
+ t_s4u t_s4u2proxy_krb5 t_saslname t_spnego t_srcattrs
check-unix: t_oid
$(RUN_TEST) ./t_invalid
@@ -43,11 +43,12 @@ check-unix: t_oid
$(RUN_TEST) ./t_prf
$(RUN_TEST) ./t_imp_name
-check-pytests: ccinit ccrefresh t_accname t_add_cred t_ccselect t_ciflags \
- t_context t_credstore t_enctypes t_err t_export_cred t_export_name \
- t_imp_cred t_inq_cred t_inq_ctx t_inq_mechs_name t_iov t_lifetime \
- t_pcontok t_s4u t_s4u2proxy_krb5 t_spnego t_srcattrs
+check-pytests: ccinit ccrefresh t_accname t_add_cred t_bindings t_ccselect \
+ t_ciflags t_context t_credstore t_enctypes t_err t_export_cred \
+ t_export_name t_imp_cred t_inq_cred t_inq_ctx t_inq_mechs_name t_iov \
+ t_lifetime t_pcontok t_s4u t_s4u2proxy_krb5 t_spnego t_srcattrs
$(RUNPYTEST) $(srcdir)/t_gssapi.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_bindings.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_ccselect.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_client_keytab.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_enctypes.py $(PYTESTFLAGS)
@@ -64,6 +65,8 @@ t_accname: t_accname.o $(COMMON_DEPS)
$(CC_LINK) -o $@ t_accname.o $(COMMON_LIBS)
t_add_cred: t_add_cred.o $(COMMON_DEPS)
$(CC_LINK) -o $@ t_add_cred.o $(COMMON_LIBS)
+t_bindings: t_bindings.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_bindings.o $(COMMON_LIBS)
t_ccselect: t_ccselect.o $(COMMON_DEPS)
$(CC_LINK) -o $@ t_ccselect.o $(COMMON_LIBS)
t_ciflags: t_ciflags.o $(COMMON_DEPS)
@@ -118,8 +121,8 @@ t_srcattrs: t_srcattrs.o $(COMMON_DEPS)
$(CC_LINK) -o $@ t_srcattrs.o $(COMMON_LIBS)
clean:
- $(RM) ccinit ccrefresh t_accname t_add_cred t_ccselect t_ciflags
- $(RM) t_context t_credstore t_enctypes t_err t_export_cred
+ $(RM) ccinit ccrefresh t_accname t_add_cred t_bindings t_ccselect
+ $(RM) t_ciflags t_context t_credstore t_enctypes t_err t_export_cred
$(RM) t_export_name t_gssexts t_imp_cred t_imp_name t_invalid
$(RM) t_inq_cred t_inq_ctx t_inq_mechs_name t_iov t_lifetime
$(RM) t_namingexts t_oid t_pcontok t_prf t_s4u t_s4u2proxy_krb5
diff --git a/src/tests/gssapi/common.c b/src/tests/gssapi/common.c
index 83e9d9bb8..7ba72f7b2 100644
--- a/src/tests/gssapi/common.c
+++ b/src/tests/gssapi/common.c
@@ -115,6 +115,20 @@ establish_contexts(gss_OID imech, gss_cred_id_t icred, gss_cred_id_t acred,
gss_name_t tname, OM_uint32 flags, gss_ctx_id_t *ictx,
gss_ctx_id_t *actx, gss_name_t *src_name, gss_OID *amech,
gss_cred_id_t *deleg_cred)
+{
+ return establish_contexts_ex(imech, icred, acred, tname, flags, ictx, actx,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_CHANNEL_BINDINGS, NULL, src_name,
+ amech, deleg_cred);
+}
+
+void
+establish_contexts_ex(gss_OID imech, gss_cred_id_t icred, gss_cred_id_t acred,
+ gss_name_t tname, OM_uint32 flags, gss_ctx_id_t *ictx,
+ gss_ctx_id_t *actx, gss_channel_bindings_t icb,
+ gss_channel_bindings_t acb, OM_uint32 *aret_flags,
+ gss_name_t *src_name, gss_OID *amech,
+ gss_cred_id_t *deleg_cred)
{
OM_uint32 minor, imaj, amaj;
gss_buffer_desc itok, atok;
@@ -126,17 +140,16 @@ establish_contexts(gss_OID imech, gss_cred_id_t icred, gss_cred_id_t acred,
for (;;) {
(void)gss_release_buffer(&minor, &itok);
imaj = gss_init_sec_context(&minor, icred, ictx, tname, imech, flags,
- GSS_C_INDEFINITE,
- GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL,
- &itok, NULL, NULL);
+ GSS_C_INDEFINITE, icb, &atok, NULL, &itok,
+ NULL, NULL);
check_gsserr("gss_init_sec_context", imaj, minor);
if (amaj == GSS_S_COMPLETE)
break;
(void)gss_release_buffer(&minor, &atok);
- amaj = gss_accept_sec_context(&minor, actx, acred, &itok,
- GSS_C_NO_CHANNEL_BINDINGS, src_name,
- amech, &atok, NULL, NULL, deleg_cred);
+ amaj = gss_accept_sec_context(&minor, actx, acred, &itok, acb,
+ src_name, amech, &atok, aret_flags, NULL,
+ deleg_cred);
check_gsserr("gss_accept_sec_context", amaj, minor);
(void)gss_release_buffer(&minor, &itok);
if (imaj == GSS_S_COMPLETE)
diff --git a/src/tests/gssapi/common.h b/src/tests/gssapi/common.h
index ae11b51d4..a5c8f87e6 100644
--- a/src/tests/gssapi/common.h
+++ b/src/tests/gssapi/common.h
@@ -62,6 +62,15 @@ void establish_contexts(gss_OID imech, gss_cred_id_t icred,
gss_name_t *src_name, gss_OID *amech,
gss_cred_id_t *deleg_cred);
+/* Establish contexts with channel bindings. */
+void establish_contexts_ex(gss_OID imech, gss_cred_id_t icred,
+ gss_cred_id_t acred, gss_name_t tname,
+ OM_uint32 flags, gss_ctx_id_t *ictx,
+ gss_ctx_id_t *actx, gss_channel_bindings_t icb,
+ gss_channel_bindings_t acb, OM_uint32 *aret_flags,
+ gss_name_t *src_name, gss_OID *amech,
+ gss_cred_id_t *deleg_cred);
+
/* Export *cred to a token, then release *cred and replace it by re-importing
* the token. */
void export_import_cred(gss_cred_id_t *cred);
diff --git a/src/tests/gssapi/deps b/src/tests/gssapi/deps
index acd0e96f8..73e4d9a74 100644
--- a/src/tests/gssapi/deps
+++ b/src/tests/gssapi/deps
@@ -33,6 +33,10 @@ $(OUTPRE)t_add_cred.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
$(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
$(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
common.h t_add_cred.c
+$(OUTPRE)t_bindings.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_bindings.c
$(OUTPRE)t_ccselect.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
$(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
$(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
diff --git a/src/tests/gssapi/t_bindings.c b/src/tests/gssapi/t_bindings.c
new file mode 100644
index 000000000..e8906715b
--- /dev/null
+++ b/src/tests/gssapi/t_bindings.c
@@ -0,0 +1,111 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2020 by Red Hat, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "common.h"
+
+/*
+ * Establish contexts (without and with GSS_C_DCE_STYLE) with the default
+ * initiator name, a specified principal name as target name, initiator
+ * bindings, and acceptor bindings. If any call is unsuccessful, display an
+ * error message. Output "yes" or "no" to indicate whether the contexts were
+ * reported as channel-bound on the acceptor. Exit with status 0 if all
+ * operations are successful, or 1 if not.
+ *
+ * Usage: ./t_bindings [-s] targetname icb acb
+ *
+ * An icb or abc value of "-" will not specify channel bindings.
+ */
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, flags1, flags2;
+ gss_name_t target_name;
+ gss_ctx_id_t ictx, actx;
+ struct gss_channel_bindings_struct icb_data = {0}, acb_data = {0};
+ gss_channel_bindings_t icb = GSS_C_NO_CHANNEL_BINDINGS;
+ gss_channel_bindings_t acb = GSS_C_NO_CHANNEL_BINDINGS;
+ gss_OID_desc *mech;
+
+ argv++;
+ argc--;
+ if (*argv != NULL && strcmp(*argv, "-s") == 0) {
+ mech = &mech_spnego;
+ argv++;
+ argc--;
+ } else {
+ mech = &mech_krb5;
+ }
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: t_bindings [-s] targetname icb acb\n");
+ return 1;
+ }
+
+ target_name = import_name(argv[0]);
+
+ if (strcmp(argv[1], "-") != 0) {
+ icb_data.application_data.length = strlen(argv[1]);
+ icb_data.application_data.value = argv[1];
+ icb = &icb_data;
+ }
+
+ if (strcmp(argv[2], "-") != 0) {
+ acb_data.application_data.length = strlen(argv[2]);
+ acb_data.application_data.value = argv[2];
+ acb = &acb_data;
+ }
+
+ establish_contexts_ex(mech, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
+ target_name, 0, &ictx, &actx, icb, acb, &flags1,
+ NULL, NULL, NULL);
+
+ /* Try again with GSS_C_DCE_STYLE */
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+
+ establish_contexts_ex(mech, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
+ target_name, GSS_C_DCE_STYLE, &ictx, &actx, icb, acb,
+ &flags2, NULL, NULL, NULL);
+ assert((flags1 & GSS_C_CHANNEL_BOUND_FLAG) ==
+ (flags2 & GSS_C_CHANNEL_BOUND_FLAG));
+ printf("%s\n", (flags1 & GSS_C_CHANNEL_BOUND_FLAG) ? "yes" : "no");
+
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+ (void)gss_release_name(&minor, &target_name);
+
+ return 0;
+}
diff --git a/src/tests/gssapi/t_bindings.py b/src/tests/gssapi/t_bindings.py
new file mode 100644
index 000000000..f377977b6
--- /dev/null
+++ b/src/tests/gssapi/t_bindings.py
@@ -0,0 +1,43 @@
+from k5test import *
+
+realm = K5Realm()
+server = 'p:' + realm.host_princ
+
+mark('krb5 channel bindings')
+realm.run(['./t_bindings', server, '-', '-'], expected_msg='no')
+realm.run(['./t_bindings', server, 'a', '-'], expected_msg='no')
+realm.run(['./t_bindings', server, 'a', 'a'], expected_msg='yes')
+realm.run(['./t_bindings', server, '-', 'a'], expected_msg='no')
+realm.run(['./t_bindings', server, 'a', 'x'],
+ expected_code=1, expected_msg='Incorrect channel bindings')
+
+mark('SPNEGO channel bindings')
+realm.run(['./t_bindings', '-s', server, '-', '-'], expected_msg='no')
+realm.run(['./t_bindings', '-s', server, 'a', '-'], expected_msg='no')
+realm.run(['./t_bindings', '-s', server, 'a', 'a'], expected_msg='yes')
+realm.run(['./t_bindings', '-s', server, '-', 'a'], expected_msg='no')
+realm.run(['./t_bindings', '-s', server, 'a', 'x'],
+ expected_code=1, expected_msg='Incorrect channel bindings')
+
+client_aware_conf = {'libdefaults': {'client_aware_channel_bindings': 'true'}}
+e = realm.special_env('cb_aware', False, krb5_conf=client_aware_conf)
+
+mark('krb5 client_aware_channel_bindings')
+realm.run(['./t_bindings', server, '-', '-'], env=e, expected_msg='no')
+realm.run(['./t_bindings', server, 'a', '-'], env=e, expected_msg='no')
+realm.run(['./t_bindings', server, 'a', 'a'], env=e, expected_msg='yes')
+realm.run(['./t_bindings', server, '-', 'a'], env=e,
+ expected_code=1, expected_msg='Incorrect channel bindings')
+realm.run(['./t_bindings', server, 'a', 'x'], env=e,
+ expected_code=1, expected_msg='Incorrect channel bindings')
+
+mark('SPNEGO client_aware_channel_bindings')
+realm.run(['./t_bindings', '-s', server, '-', '-'], env=e, expected_msg='no')
+realm.run(['./t_bindings', '-s', server, 'a', '-'], env=e, expected_msg='no')
+realm.run(['./t_bindings', '-s', server, 'a', 'a'], env=e, expected_msg='yes')
+realm.run(['./t_bindings', '-s', server, '-', 'a'], env=e,
+ expected_code=1, expected_msg='Incorrect channel bindings')
+realm.run(['./t_bindings', '-s', server, 'a', 'x'], env=e,
+ expected_code=1, expected_msg='Incorrect channel bindings')
+
+success('channel bindings tests')
diff --git a/src/tests/gssapi/t_negoex.py b/src/tests/gssapi/t_negoex.py
index 88470d2fa..a218899c4 100644
--- a/src/tests/gssapi/t_negoex.py
+++ b/src/tests/gssapi/t_negoex.py
@@ -139,4 +139,11 @@ msgs = ('sending [3]AP_REQUEST', 'sending [7]CHALLENGE', 'sending [8]VERIFY',
'sending [11]CHALLENGE', 'sending [12]VERIFY', 'sending [13]VERIFY')
test({'HOPS': '4', 'KEY': 'accept-always'}, expected_trace=())
+mark('channel bindings')
+e = realm.env.copy()
+e.update({'HOPS': '1', 'GSS_INIT_BINDING': 'a', 'GSS_ACCEPT_BINDING': 'b'})
+# The test mech will verify that the bindings are communicated to the
+# mech, but does not set the channel-bound flag.
+realm.run(['./t_bindings', '-s', 'h:host', 'a', 'b'], env=e, expected_msg='no')
+
success('NegoEx tests')

View File

@ -0,0 +1,265 @@
From 64f643a7f798c5528182dc068f15dca7b3f2d8a1 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Tue, 10 Mar 2020 13:13:17 +0100
Subject: [PATCH] Add client_aware_channel_bindings option
Add client support for KERB_AP_OPTIONS_CBT in the form of a profile
option "client_aware_gss_bindings". Adjust the make_etype_list()
helper so that enctype negotiation and AP_OPTIONS can be included in
the same IF-RELEVANT wrapper.
[ghudson@mit.edu: refactored; edited documentation; wrote commit
message]
ticket: 8900
(cherry picked from commit 225e6ef7f021cd1a8ef2a054af0ca58b7288fd81)
(cherry picked from commit 2a08fe3d2d1972df4ffe37d4bb64b161889ff988)
---
doc/admin/conf_files/krb5_conf.rst | 6 +
src/include/k5-int.h | 1 +
src/lib/krb5/krb/mk_req_ext.c | 177 +++++++++++++++--------------
3 files changed, 98 insertions(+), 86 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 3a8b9cf47..315253e37 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -389,6 +389,12 @@ The libdefaults section may contain any of the following relations:
credentials will fail if the client machine does not have a
keytab. The default value is false.
+**client_aware_channel_bindings**
+ If this flag is true, then all application protocol authentication
+ requests will be flagged to indicate that the application supports
+ channel bindings when operating over a secure channel. The
+ default value is false.
+
.. _realms:
[realms]
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 0d9af3d95..eb18a4cd6 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -299,6 +299,7 @@ typedef unsigned char u_char;
#define KRB5_CONF_V4_INSTANCE_CONVERT "v4_instance_convert"
#define KRB5_CONF_V4_REALM "v4_realm"
#define KRB5_CONF_VERIFY_AP_REQ_NOFAIL "verify_ap_req_nofail"
+#define KRB5_CONF_CLIENT_AWARE_GSS_BINDINGS "client_aware_channel_bindings"
/* Cache configuration variables */
#define KRB5_CC_CONF_FAST_AVAIL "fast_avail"
diff --git a/src/lib/krb5/krb/mk_req_ext.c b/src/lib/krb5/krb/mk_req_ext.c
index 9fc6a0e52..08504860c 100644
--- a/src/lib/krb5/krb/mk_req_ext.c
+++ b/src/lib/krb5/krb/mk_req_ext.c
@@ -68,10 +68,9 @@
*/
static krb5_error_code
-make_etype_list(krb5_context context,
- krb5_enctype *desired_etypes,
- krb5_enctype tkt_enctype,
- krb5_authdata ***authdata);
+make_ap_authdata(krb5_context context, krb5_enctype *desired_enctypes,
+ krb5_enctype tkt_enctype, krb5_boolean client_aware_cb,
+ krb5_authdata ***authdata_out);
static krb5_error_code
generate_authenticator(krb5_context,
@@ -263,7 +262,8 @@ generate_authenticator(krb5_context context, krb5_authenticator *authent,
krb5_enctype tkt_enctype)
{
krb5_error_code retval;
- krb5_authdata **ext_authdata = NULL;
+ krb5_authdata **ext_authdata = NULL, **ap_authdata, **combined;
+ int client_aware_cb;
authent->client = client;
authent->checksum = cksum;
@@ -297,99 +297,104 @@ generate_authenticator(krb5_context context, krb5_authenticator *authent,
krb5_free_authdata(context, ext_authdata);
}
- /* Only send EtypeList if we prefer another enctype to tkt_enctype */
- if (desired_etypes != NULL && desired_etypes[0] != tkt_enctype) {
- TRACE_MK_REQ_ETYPES(context, desired_etypes);
- retval = make_etype_list(context, desired_etypes, tkt_enctype,
- &authent->authorization_data);
+ retval = profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS,
+ KRB5_CONF_CLIENT_AWARE_GSS_BINDINGS, NULL,
+ FALSE, &client_aware_cb);
+ if (retval)
+ return retval;
+
+ /* Add etype negotiation or channel-binding awareness authdata to the
+ * front, if appropriate. */
+ retval = make_ap_authdata(context, desired_etypes, tkt_enctype,
+ client_aware_cb, &ap_authdata);
+ if (retval)
+ return retval;
+ if (ap_authdata != NULL) {
+ retval = krb5_merge_authdata(context, ap_authdata,
+ authent->authorization_data, &combined);
+ krb5_free_authdata(context, ap_authdata);
if (retval)
return retval;
+ krb5_free_authdata(context, authent->authorization_data);
+ authent->authorization_data = combined;
}
return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
}
-/* RFC 4537 */
+/* Set *out to a DER-encoded RFC 4537 etype list, or to NULL if no etype list
+ * should be sent. */
static krb5_error_code
-make_etype_list(krb5_context context,
- krb5_enctype *desired_etypes,
- krb5_enctype tkt_enctype,
- krb5_authdata ***authdata)
+make_etype_list(krb5_context context, krb5_enctype *desired_enctypes,
+ krb5_enctype tkt_enctype, krb5_data **out)
{
- krb5_error_code code;
- krb5_etype_list etypes;
- krb5_data *enc_etype_list;
- krb5_data *ad_if_relevant;
- krb5_authdata *etype_adata[2], etype_adatum, **adata;
- int i;
+ krb5_etype_list etlist;
+ int count;
- etypes.etypes = desired_etypes;
+ *out = NULL;
- for (etypes.length = 0;
- etypes.etypes[etypes.length] != ENCTYPE_NULL;
- etypes.length++)
- {
- /*
- * RFC 4537:
- *
- * If the enctype of the ticket session key is included in the enctype
- * list sent by the client, it SHOULD be the last on the list;
- */
- if (etypes.length && etypes.etypes[etypes.length - 1] == tkt_enctype)
+ /* Only send a list if we prefer another enctype to tkt_enctype. */
+ if (desired_enctypes == NULL || desired_enctypes[0] == tkt_enctype)
+ return 0;
+
+ /* Count elements of desired_etypes, stopping at tkt_enctypes if present.
+ * (Per RFC 4537, it must be the last option if it is included.) */
+ for (count = 0; desired_enctypes[count] != ENCTYPE_NULL; count++) {
+ if (count > 0 && desired_enctypes[count - 1] == tkt_enctype)
break;
}
- code = encode_krb5_etype_list(&etypes, &enc_etype_list);
- if (code) {
- return code;
- }
-
- etype_adatum.magic = KV5M_AUTHDATA;
- etype_adatum.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
- etype_adatum.length = enc_etype_list->length;
- etype_adatum.contents = (krb5_octet *)enc_etype_list->data;
-
- etype_adata[0] = &etype_adatum;
- etype_adata[1] = NULL;
-
- /* Wrap in AD-IF-RELEVANT container */
- code = encode_krb5_authdata(etype_adata, &ad_if_relevant);
- if (code) {
- krb5_free_data(context, enc_etype_list);
- return code;
- }
-
- krb5_free_data(context, enc_etype_list);
-
- adata = *authdata;
- if (adata == NULL) {
- adata = (krb5_authdata **)calloc(2, sizeof(krb5_authdata *));
- i = 0;
- } else {
- for (i = 0; adata[i] != NULL; i++)
- ;
-
- adata = (krb5_authdata **)realloc(*authdata,
- (i + 2) * sizeof(krb5_authdata *));
- }
- if (adata == NULL) {
- krb5_free_data(context, ad_if_relevant);
- return ENOMEM;
- }
- *authdata = adata;
-
- adata[i] = (krb5_authdata *)malloc(sizeof(krb5_authdata));
- if (adata[i] == NULL) {
- krb5_free_data(context, ad_if_relevant);
- return ENOMEM;
- }
- adata[i]->magic = KV5M_AUTHDATA;
- adata[i]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
- adata[i]->length = ad_if_relevant->length;
- adata[i]->contents = (krb5_octet *)ad_if_relevant->data;
- free(ad_if_relevant); /* contents owned by adata[i] */
-
- adata[i + 1] = NULL;
-
- return 0;
+ etlist.etypes = desired_enctypes;
+ etlist.length = count;
+ return encode_krb5_etype_list(&etlist, out);
+}
+
+/* Set *authdata_out to appropriate authenticator authdata for the request,
+ * encoded in a single AD_IF_RELEVANT element. */
+static krb5_error_code
+make_ap_authdata(krb5_context context, krb5_enctype *desired_enctypes,
+ krb5_enctype tkt_enctype, krb5_boolean client_aware_cb,
+ krb5_authdata ***authdata_out)
+{
+ krb5_error_code ret;
+ krb5_authdata etypes_ad, flags_ad, *list[3];
+ krb5_data *der_etypes = NULL;
+ size_t count = 0;
+ uint8_t flagbuf[4];
+ const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
+
+ *authdata_out = NULL;
+
+ /* Include an ETYPE_NEGOTIATION element if appropriate. */
+ ret = make_etype_list(context, desired_enctypes, tkt_enctype, &der_etypes);
+ if (ret)
+ goto cleanup;
+ if (der_etypes != NULL) {
+ etypes_ad.magic = KV5M_AUTHDATA;
+ etypes_ad.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
+ etypes_ad.length = der_etypes->length;
+ etypes_ad.contents = (uint8_t *)der_etypes->data;
+ list[count++] = &etypes_ad;
+ }
+
+ /* Include an AP_OPTIONS element if the CBT flag is configured. */
+ if (client_aware_cb != 0) {
+ store_32_le(KERB_AP_OPTIONS_CBT, flagbuf);
+ flags_ad.magic = KV5M_AUTHDATA;
+ flags_ad.ad_type = KRB5_AUTHDATA_AP_OPTIONS;
+ flags_ad.length = 4;
+ flags_ad.contents = flagbuf;
+ list[count++] = &flags_ad;
+ }
+
+ if (count > 0) {
+ list[count] = NULL;
+ ret = krb5_encode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ list, authdata_out);
+ }
+
+cleanup:
+ krb5_free_data(context, der_etypes);
+ return ret;
}

View File

@ -1,31 +0,0 @@
From c891e4bc54c8083a1af8d28aa9b12ab1177ebb9a Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 27 Mar 2018 00:49:43 -0400
Subject: [PATCH] Add doc index entries for SPAKE constants
ticket: 8647
(cherry picked from commit c010c9031753f356bb380e8a1324cc34721f8221)
---
doc/appdev/refs/macros/index.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index dba818b26..47c6d4413 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -190,6 +190,7 @@ Public
KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM.rst
KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID.rst
KRB5_KEYUSAGE_PA_SAM_RESPONSE.rst
+ KRB5_KEYUSAGE_SPAKE.rst
KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY.rst
KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY.rst
KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY.rst
@@ -274,6 +275,7 @@ Public
KRB5_PADATA_SAM_RESPONSE.rst
KRB5_PADATA_SAM_RESPONSE_2.rst
KRB5_PADATA_SESAME.rst
+ KRB5_PADATA_SPAKE.rst
KRB5_PADATA_SVR_REFERRAL_INFO.rst
KRB5_PADATA_TGS_REQ.rst
KRB5_PADATA_USE_SPECIFIED_KVNO.rst

View File

@ -0,0 +1,54 @@
From 9a9ab4b2cad1597cbafbae756483aefa6e36f1eb Mon Sep 17 00:00:00 2001
From: Jiri Sasek <Jiri.Sasek@Oracle.COM>
Date: Fri, 13 Mar 2020 19:02:58 +0100
Subject: [PATCH] Add finalization safety check to com_err
If the linker erroneously runs the libkrb5 finalizer after the
libcom_err finalizer, the consequent remove_error_table() calls could
crash due to accessing a destroyed mutex or an invalid et_list
pointer. Add an unsynchronized check on finalized in
remove_error_table(), and set et_list to null in com_err_terminate()
after destroying the list.
[ghudson@mit.edu: minimized code hanges; rewrote comment and commit
message]
ticket: 8890 (new)
(cherry picked from commit 9d654aa05e26bbf22f140abde3436afeff2fdf8d)
(cherry picked from commit c7a37d3e87132864ebc44710baf1d50a69682b5c)
---
src/util/et/error_message.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/util/et/error_message.c b/src/util/et/error_message.c
index d7069a9df..7dc02a34e 100644
--- a/src/util/et/error_message.c
+++ b/src/util/et/error_message.c
@@ -26,7 +26,7 @@
static struct et_list *et_list;
static k5_mutex_t et_list_lock = K5_MUTEX_PARTIAL_INITIALIZER;
-static int terminated = 0; /* for debugging shlib fini sequence errors */
+static int terminated = 0; /* for safety and finalization debugging */
MAKE_INIT_FUNCTION(com_err_initialize);
MAKE_FINI_FUNCTION(com_err_terminate);
@@ -69,6 +69,7 @@ void com_err_terminate(void)
enext = e->next;
free(e);
}
+ et_list = NULL;
k5_mutex_unlock(&et_list_lock);
k5_mutex_destroy(&et_list_lock);
terminated = 1;
@@ -280,6 +281,10 @@ remove_error_table(const struct error_table *et)
{
struct et_list **ep, *e;
+ /* Safety check in case libraries are finalized in the wrong order. */
+ if (terminated)
+ return ENOENT;
+
if (CALL_INIT_FUNCTION(com_err_initialize))
return 0;
k5_mutex_lock(&et_list_lock);

View File

@ -1,204 +0,0 @@
From f44ef4893050e673f495444c27a19525813f75a8 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 11 Jun 2018 13:53:27 -0400
Subject: [PATCH] Add flag to disable encrypted timestamp on client
ticket: 8655
(cherry picked from commit 4ad376134b8d456392edbac7a7d351e6c7a7f0e7)
---
doc/admin/conf_files/krb5_conf.rst | 10 ++++++++++
doc/admin/spake.rst | 8 ++++++++
src/include/k5-int.h | 1 +
src/include/k5-trace.h | 2 ++
src/lib/krb5/krb/get_in_tkt.c | 23 +++++++++++++++++++++++
src/lib/krb5/krb/init_creds_ctx.h | 1 +
src/lib/krb5/krb/preauth_encts.c | 14 +++++++++++++-
src/tests/t_referral.py | 13 +++++++++++++
8 files changed, 71 insertions(+), 1 deletion(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index ce545492d..eb5c29e5d 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -475,6 +475,16 @@ following tags may be specified in the realm's subsection:
(for example, when converting ``rcmd.hostname`` to
``host/hostname.domain``).
+**disable_encrypted_timestamp**
+ If this flag is true, the client will not perform encrypted
+ timestamp preauthentication if requested by the KDC. Setting this
+ flag can help to prevent dictionary attacks by active attackers,
+ if the realm's KDCs support SPAKE preauthentication or if initial
+ authentication always uses another mechanism or always uses FAST.
+ This flag persists across client referrals during initial
+ authentication. This flag does not prevent the KDC from offering
+ encrypted timestamp. New in release 1.17.
+
**http_anchors**
When KDCs and kpasswd servers are accessed through HTTPS proxies, this tag
can be used to specify the location of the CA certificate which should be
diff --git a/doc/admin/spake.rst b/doc/admin/spake.rst
index b65c694aa..4f6eeaf53 100644
--- a/doc/admin/spake.rst
+++ b/doc/admin/spake.rst
@@ -30,6 +30,14 @@ principal entries, as you would for any preauthentication mechanism::
Clients which do not implement SPAKE preauthentication will fall back
to encrypted timestamp.
+An active attacker can force a fallback to encrypted timestamp by
+modifying the initial KDC response, defeating the protection against
+dictionary attacks. To prevent this fallback on clients which do
+implement SPAKE preauthentication, set the
+**disable_encrypted_timestamp** variable to ``true`` in the
+:ref:`realms` subsection for realms whose KDCs offer SPAKE
+preauthentication.
+
By default, SPAKE preauthentication requires an extra network round
trip to the KDC during initial authentication. If most of the clients
in a realm support SPAKE, this extra round trip can be eliminated
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 86b53c76b..e4a9a1412 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -204,6 +204,7 @@ typedef unsigned char u_char;
#define KRB5_CONF_DES_CRC_SESSION_SUPPORTED "des_crc_session_supported"
#define KRB5_CONF_DICT_FILE "dict_file"
#define KRB5_CONF_DISABLE "disable"
+#define KRB5_CONF_DISABLE_ENCRYPTED_TIMESTAMP "disable_encrypted_timestamp"
#define KRB5_CONF_DISABLE_LAST_SUCCESS "disable_last_success"
#define KRB5_CONF_DISABLE_LOCKOUT "disable_lockout"
#define KRB5_CONF_DNS_CANONICALIZE_HOSTNAME "dns_canonicalize_hostname"
diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
index 5f7eb9517..0854974dc 100644
--- a/src/include/k5-trace.h
+++ b/src/include/k5-trace.h
@@ -299,6 +299,8 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
#define TRACE_PREAUTH_ENC_TS(c, sec, usec, plain, enc) \
TRACE(c, "Encrypted timestamp (for {long}.{int}): plain {hexdata}, " \
"encrypted {hexdata}", (long) sec, (int) usec, plain, enc)
+#define TRACE_PREAUTH_ENC_TS_DISABLED(c) \
+ TRACE(c, "Ignoring encrypted timestamp because it is disabled")
#define TRACE_PREAUTH_ETYPE_INFO(c, etype, salt, s2kparams) \
TRACE(c, "Selected etype info: etype {etype}, salt \"{data}\", " \
"params \"{data}\"", etype, salt, s2kparams)
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index c026bbc6d..79dede2c6 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -801,6 +801,24 @@ read_allowed_preauth_type(krb5_context context, krb5_init_creds_context ctx)
free(tmp);
}
+/* Return true if encrypted timestamp is disabled for realm. */
+static krb5_boolean
+encts_disabled(profile_t profile, const krb5_data *realm)
+{
+ krb5_error_code ret;
+ char *realmstr;
+ int bval;
+
+ realmstr = k5memdup0(realm->data, realm->length, &ret);
+ if (realmstr == NULL)
+ return FALSE;
+ ret = profile_get_boolean(profile, KRB5_CONF_REALMS, realmstr,
+ KRB5_CONF_DISABLE_ENCRYPTED_TIMESTAMP, FALSE,
+ &bval);
+ free(realmstr);
+ return (ret == 0) ? bval : FALSE;
+}
+
/**
* Throw away any pre-authentication realm state and begin with a
* unauthenticated or optimistically authenticated request. If fast_upgrade is
@@ -842,6 +860,11 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
goto cleanup;
}
+ /* Never set encts_disabled back to false, so it can't be circumvented with
+ * client realm referrals. */
+ if (encts_disabled(context->profile, &ctx->request->client->realm))
+ ctx->encts_disabled = TRUE;
+
krb5_free_principal(context, ctx->request->server);
ctx->request->server = NULL;
diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
index 7ba61e17c..7a6219b1c 100644
--- a/src/lib/krb5/krb/init_creds_ctx.h
+++ b/src/lib/krb5/krb/init_creds_ctx.h
@@ -61,6 +61,7 @@ struct _krb5_init_creds_context {
krb5_boolean info_pa_permitted;
krb5_boolean restarted;
krb5_boolean fallback_disabled;
+ krb5_boolean encts_disabled;
struct krb5_responder_context_st rctx;
krb5_preauthtype selected_preauth_type;
krb5_preauthtype allowed_preauth_type;
diff --git a/src/lib/krb5/krb/preauth_encts.c b/src/lib/krb5/krb/preauth_encts.c
index 45bf9da92..345701984 100644
--- a/src/lib/krb5/krb/preauth_encts.c
+++ b/src/lib/krb5/krb/preauth_encts.c
@@ -28,6 +28,7 @@
#include <k5-int.h>
#include <krb5/clpreauth_plugin.h>
#include "int-proto.h"
+#include "init_creds_ctx.h"
static krb5_error_code
encts_prep_questions(krb5_context context, krb5_clpreauth_moddata moddata,
@@ -38,7 +39,10 @@ encts_prep_questions(krb5_context context, krb5_clpreauth_moddata moddata,
krb5_data *encoded_previous_request,
krb5_pa_data *pa_data)
{
- cb->need_as_key(context, rock);
+ krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
+
+ if (!ctx->encts_disabled)
+ cb->need_as_key(context, rock);
return 0;
}
@@ -51,6 +55,7 @@ encts_process(krb5_context context, krb5_clpreauth_moddata moddata,
krb5_prompter_fct prompter, void *prompter_data,
krb5_pa_data ***out_padata)
{
+ krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
krb5_error_code ret;
krb5_pa_enc_ts pa_enc;
krb5_data *ts = NULL, *enc_ts = NULL;
@@ -60,6 +65,13 @@ encts_process(krb5_context context, krb5_clpreauth_moddata moddata,
enc_data.ciphertext = empty_data();
+ if (ctx->encts_disabled) {
+ TRACE_PREAUTH_ENC_TS_DISABLED(context);
+ k5_setmsg(context, KRB5_PREAUTH_FAILED,
+ _("Encrypted timestamp is disabled"));
+ return KRB5_PREAUTH_FAILED;
+ }
+
ret = cb->get_as_key(context, rock, &as_key);
if (ret)
goto cleanup;
diff --git a/src/tests/t_referral.py b/src/tests/t_referral.py
index 98fdf2925..e12fdc2e9 100755
--- a/src/tests/t_referral.py
+++ b/src/tests/t_referral.py
@@ -126,4 +126,17 @@ r1.klist('user@KRBTEST2.COM', 'krbtgt/KRBTEST2.COM')
r1.kinit('abc@XYZ', 'pw', ['-E'])
r1.klist('abc\@XYZ@KRBTEST2.COM', 'krbtgt/KRBTEST2.COM')
+# Test that disable_encrypted_timestamp persists across client
+# referrals. (This test relies on SPAKE not being enabled by default
+# on the KDC.)
+r2.run([kadminl, 'modprinc', '+preauth', 'user'])
+msgs = ('Encrypted timestamp (for ')
+r1.kinit('user', password('user'), ['-C'], expected_trace=msgs)
+dconf = {'realms': {'$realm': {'disable_encrypted_timestamp': 'true'}}}
+denv = r1.special_env('disable_encts', False, krb5_conf=dconf)
+msgs = ('Ignoring encrypted timestamp because it is disabled',
+ '/Encrypted timestamp is disabled')
+r1.kinit('user', None, ['-C'], env=denv, expected_code=1, expected_trace=msgs,
+ expected_msg='Encrypted timestamp is disabled')
+
success('KDC host referral tests')

View File

@ -1,119 +0,0 @@
From 74e1079df0cc6e8932e487455177a69f782b863a Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 4 Jan 2018 14:35:12 -0500
Subject: [PATCH] Add k5_buf_add_vfmt to k5buf interface
(cherry picked from commit f05766469efc2a055085c0bcf9d40c4cdf47fe36)
---
src/include/k5-buf.h | 8 ++++++
src/util/support/k5buf.c | 26 +++++++++++--------
src/util/support/libkrb5support-fixed.exports | 1 +
3 files changed, 24 insertions(+), 11 deletions(-)
diff --git a/src/include/k5-buf.h b/src/include/k5-buf.h
index f3207bd09..1223916a6 100644
--- a/src/include/k5-buf.h
+++ b/src/include/k5-buf.h
@@ -76,6 +76,14 @@ void k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
#endif
;
+/* Add sprintf-style formatted data to BUF, with a va_list. The value of ap is
+ * undefined after the call. */
+void k5_buf_add_vfmt(struct k5buf *buf, const char *fmt, va_list ap)
+#if !defined(__cplusplus) && (__GNUC__ > 2)
+ __attribute__((__format__(__printf__, 2, 0)))
+#endif
+ ;
+
/* Extend the length of buf by len and return a pointer to the reserved space,
* to be filled in by the caller. Return NULL on error. */
void *k5_buf_get_space(struct k5buf *buf, size_t len);
diff --git a/src/util/support/k5buf.c b/src/util/support/k5buf.c
index f619f6a48..35978f238 100644
--- a/src/util/support/k5buf.c
+++ b/src/util/support/k5buf.c
@@ -141,9 +141,9 @@ k5_buf_add_len(struct k5buf *buf, const void *data, size_t len)
}
void
-k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
+k5_buf_add_vfmt(struct k5buf *buf, const char *fmt, va_list ap)
{
- va_list ap;
+ va_list apcopy;
int r;
size_t remaining;
char *tmp;
@@ -154,9 +154,7 @@ k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
if (buf->buftype == K5BUF_FIXED) {
/* Format the data directly into the fixed buffer. */
- va_start(ap, fmt);
r = vsnprintf(endptr(buf), remaining, fmt, ap);
- va_end(ap);
if (SNPRINTF_OVERFLOW(r, remaining))
set_error(buf);
else
@@ -166,9 +164,9 @@ k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
/* Optimistically format the data directly into the dynamic buffer. */
assert(buf->buftype == K5BUF_DYNAMIC);
- va_start(ap, fmt);
- r = vsnprintf(endptr(buf), remaining, fmt, ap);
- va_end(ap);
+ va_copy(apcopy, ap);
+ r = vsnprintf(endptr(buf), remaining, fmt, apcopy);
+ va_end(apcopy);
if (!SNPRINTF_OVERFLOW(r, remaining)) {
buf->len += (unsigned int) r;
return;
@@ -179,9 +177,7 @@ k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
if (!ensure_space(buf, r))
return;
remaining = buf->space - buf->len;
- va_start(ap, fmt);
r = vsnprintf(endptr(buf), remaining, fmt, ap);
- va_end(ap);
if (SNPRINTF_OVERFLOW(r, remaining)) /* Shouldn't ever happen. */
k5_buf_free(buf);
else
@@ -191,9 +187,7 @@ k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
/* It's a pre-C99 snprintf implementation, or something else went wrong.
* Fall back to asprintf. */
- va_start(ap, fmt);
r = vasprintf(&tmp, fmt, ap);
- va_end(ap);
if (r < 0) {
k5_buf_free(buf);
return;
@@ -206,6 +200,16 @@ k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
free(tmp);
}
+void
+k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ k5_buf_add_vfmt(buf, fmt, ap);
+ va_end(ap);
+}
+
void *
k5_buf_get_space(struct k5buf *buf, size_t len)
{
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index 30c946e7e..cb9bf0826 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -6,6 +6,7 @@ k5_buf_init_dynamic
k5_buf_add
k5_buf_add_len
k5_buf_add_fmt
+k5_buf_add_vfmt
k5_buf_get_space
k5_buf_truncate
k5_buf_status

View File

@ -1,222 +0,0 @@
From 9010a0dbf59771cb0a9c1e6fd5a18a92a1200ca7 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 5 Jun 2018 14:01:05 -0400
Subject: [PATCH] Add k5_dir_filenames() to libkrb5support
Add a support function to get a list of filenames from a directory in
sorted order.
(cherry picked from commit 27534121eb39089ff4335d8b465027e9ba783682)
---
src/include/k5-platform.h | 7 +
src/util/support/Makefile.in | 3 +
src/util/support/dir_filenames.c | 135 ++++++++++++++++++
src/util/support/libkrb5support-fixed.exports | 2 +
4 files changed, 147 insertions(+)
create mode 100644 src/util/support/dir_filenames.c
diff --git a/src/include/k5-platform.h b/src/include/k5-platform.h
index 07ef6a4ca..763408a09 100644
--- a/src/include/k5-platform.h
+++ b/src/include/k5-platform.h
@@ -44,6 +44,8 @@
* + constant time memory comparison
* + path manipulation
* + _, N_, dgettext, bindtextdomain (for localization)
+ * + getopt_long
+ * + fetching filenames from a directory
*/
#ifndef K5_PLATFORM_H
@@ -1148,4 +1150,9 @@ extern int k5_getopt_long(int nargc, char **nargv, char *options,
#define getopt_long k5_getopt_long
#endif /* HAVE_GETOPT_LONG */
+/* Set *fnames_out to a null-terminated list of filenames within dirname,
+ * sorted according to strcmp(). Return 0 on success, or ENOENT/ENOMEM. */
+int k5_dir_filenames(const char *dirname, char ***fnames_out);
+void k5_free_filenames(char **fnames);
+
#endif /* K5_PLATFORM_H */
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
index caaf15822..4715e0391 100644
--- a/src/util/support/Makefile.in
+++ b/src/util/support/Makefile.in
@@ -85,6 +85,7 @@ STLIBOBJS= \
hex.o \
bcmp.o \
strerror_r.o \
+ dir_filenames.o \
$(GETTIMEOFDAY_ST_OBJ) \
$(IPC_ST_OBJ) \
$(STRLCPY_ST_OBJ) \
@@ -111,6 +112,7 @@ LIBOBJS= \
$(OUTPRE)hex.$(OBJEXT) \
$(OUTPRE)bcmp.$(OBJEXT) \
$(OUTPRE)strerror_r.$(OBJEXT) \
+ $(OUTPRE)dir_filenames.$(OBJEXT) \
$(GETTIMEOFDAY_OBJ) \
$(IPC_OBJ) \
$(STRLCPY_OBJ) \
@@ -147,6 +149,7 @@ SRCS=\
$(srcdir)/hex.c \
$(srcdir)/bcmp.c \
$(srcdir)/strerror_r.c \
+ $(srcdir)/dir_filenames.c \
$(srcdir)/t_utf8.c \
$(srcdir)/t_utf16.c \
$(srcdir)/getopt.c \
diff --git a/src/util/support/dir_filenames.c b/src/util/support/dir_filenames.c
new file mode 100644
index 000000000..9312b0238
--- /dev/null
+++ b/src/util/support/dir_filenames.c
@@ -0,0 +1,135 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/dir_filenames.c - fetch filenames in a directory */
+/*
+ * Copyright (C) 2018 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-platform.h"
+
+void
+k5_free_filenames(char **fnames)
+{
+ char **fn;
+
+ for (fn = fnames; fn != NULL && *fn != NULL; fn++)
+ free(*fn);
+ free(fnames);
+}
+
+/* Resize the filename list and add a name. */
+static int
+add_filename(char ***fnames, int *n_fnames, const char *name)
+{
+ char **newlist;
+
+ newlist = realloc(*fnames, (*n_fnames + 2) * sizeof(*newlist));
+ if (newlist == NULL)
+ return ENOMEM;
+ *fnames = newlist;
+ newlist[*n_fnames] = strdup(name);
+ if (newlist[*n_fnames] == NULL)
+ return ENOMEM;
+ (*n_fnames)++;
+ newlist[*n_fnames] = NULL;
+ return 0;
+}
+
+static int
+compare_with_strcmp(const void *a, const void *b)
+{
+ return strcmp(*(char **)a, *(char **)b);
+}
+
+#ifdef _WIN32
+
+int
+k5_dir_filenames(const char *dirname, char ***fnames_out)
+{
+ char *wildcard;
+ WIN32_FIND_DATA ffd;
+ HANDLE handle;
+ char **fnames = NULL;
+ int n_fnames = 0;
+
+ *fnames_out = NULL;
+
+ if (asprintf(&wildcard, "%s\\*", dirname) < 0)
+ return ENOMEM;
+ handle = FindFirstFile(wildcard, &ffd);
+ free(wildcard);
+ if (handle == INVALID_HANDLE_VALUE)
+ return ENOENT;
+
+ do {
+ if (add_filename(&fnames, &n_fnames, &ffd.cFileName) != 0) {
+ k5_free_filenames(fnames);
+ FindClose(handle);
+ return ENOMEM;
+ }
+ } while (FindNextFile(handle, &ffd) != 0);
+
+ FindClose(handle);
+ qsort(fnames, n_fnames, sizeof(*fnames), compare_with_strcmp);
+ *fnames_out = fnames;
+ return 0;
+}
+
+#else /* _WIN32 */
+
+#include <dirent.h>
+
+int
+k5_dir_filenames(const char *dirname, char ***fnames_out)
+{
+ DIR *dir;
+ struct dirent *ent;
+ char **fnames = NULL;
+ int n_fnames = 0;
+
+ *fnames_out = NULL;
+
+ dir = opendir(dirname);
+ if (dir == NULL)
+ return ENOENT;
+
+ while ((ent = readdir(dir)) != NULL) {
+ if (add_filename(&fnames, &n_fnames, ent->d_name) != 0) {
+ k5_free_filenames(fnames);
+ closedir(dir);
+ return ENOMEM;
+ }
+ }
+
+ closedir(dir);
+ qsort(fnames, n_fnames, sizeof(*fnames), compare_with_strcmp);
+ *fnames_out = fnames;
+ return 0;
+}
+
+#endif /* not _WIN32 */
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index a5e2ade04..16ed5a6c1 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -58,6 +58,8 @@ k5_path_split
k5_strerror_r
k5_utf8_to_utf16le
k5_utf16le_to_utf8
+k5_dir_filenames
+k5_free_filenames
krb5int_key_register
krb5int_key_delete
krb5int_getspecific

View File

@ -1,60 +0,0 @@
From 68b61c6d6402c0ad57509705137c92ae814ace27 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 18 Apr 2018 19:21:40 -0400
Subject: [PATCH] Add k5test mark() function
Make it easier to locate a failing command in long Python test scripts
by allowing the script to output marks, and displaying the most recent
mark with command failures.
(cherry picked from commit 4e813204ac3dace93297f47d64dfc0aaecc370f8)
---
src/util/k5test.py | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/util/k5test.py b/src/util/k5test.py
index 4d30baf40..bc32877a7 100644
--- a/src/util/k5test.py
+++ b/src/util/k5test.py
@@ -141,6 +141,11 @@ Scripts may use the following functions and variables:
added newline) in testlog, and write it to stdout if running
verbosely.
+* mark(message): Place a divider message in the test output, to make
+ it easier to determine what part of the test script a command
+ invocation belongs to. The last mark message will also be displayed
+ if a command invocation fails. Do not include a newline in message.
+
* which(progname): Return the location of progname in the executable
path, or None if it is not found.
@@ -376,6 +381,8 @@ def fail(msg):
"""Print a message and exit with failure."""
global _current_pass
print "*** Failure:", msg
+ if _last_mark:
+ print "*** Last mark: %s" % _last_mark
if _last_cmd:
print "*** Last command (#%d): %s" % (_cmd_index - 1, _last_cmd)
if _last_cmd_output:
@@ -392,6 +399,12 @@ def success(msg):
_success = True
+def mark(msg):
+ global _last_mark
+ output('\n====== %s ======\n' % msg)
+ _last_mark = msg
+
+
def skipped(whatmsg, whymsg):
output('*** Skipping: %s: %s\n' % (whatmsg, whymsg), force_verbose=True)
f = open(os.path.join(buildtop, 'skiptests'), 'a')
@@ -1275,6 +1288,7 @@ atexit.register(_onexit)
signal.signal(signal.SIGINT, _onsigint)
_outfile = open('testlog', 'w')
_cmd_index = 1
+_last_mark = None
_last_cmd = None
_last_cmd_output = None
buildtop = _find_buildtop()

View File

@ -1,484 +0,0 @@
From 507b1aff60fdadc91ca7c56d39711049aeeb1e58 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 19 Feb 2018 00:51:44 -0500
Subject: [PATCH] Add libkrb5support hex functions and tests
(cherry picked from commit 720dea558da0062d3cea4385327161e62cf09a5e)
[rharwood@redhat.com Remove .gitignore]
---
src/include/k5-hex.h | 53 ++++++
src/util/support/Makefile.in | 15 +-
src/util/support/deps | 6 +
src/util/support/hex.c | 116 ++++++++++++
src/util/support/libkrb5support-fixed.exports | 2 +
src/util/support/t_hex.c | 169 ++++++++++++++++++
6 files changed, 358 insertions(+), 3 deletions(-)
create mode 100644 src/include/k5-hex.h
create mode 100644 src/util/support/hex.c
create mode 100644 src/util/support/t_hex.c
diff --git a/src/include/k5-hex.h b/src/include/k5-hex.h
new file mode 100644
index 000000000..75bd2cb19
--- /dev/null
+++ b/src/include/k5-hex.h
@@ -0,0 +1,53 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-hex.h - libkrb5support hex encoding/decoding declarations */
+/*
+ * Copyright (C) 2018 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef K5_HEX_H
+#define K5_HEX_H
+
+#include "k5-platform.h"
+
+/*
+ * Encode len bytes in hex, placing the result in allocated storage in
+ * *hex_out. Use uppercase hex digits if uppercase is non-zero. Return 0 on
+ * success, ENOMEM on error.
+ */
+int k5_hex_encode(const void *bytes, size_t len, int uppercase,
+ char **hex_out);
+
+/*
+ * Decode hex bytes, placing the result in allocated storage in *bytes_out and
+ * *len_out. Null-terminate the result (primarily for decoding passwords in
+ * libkdb_ldap). Return 0 on success, ENOMEM or EINVAL on error.
+ */
+int k5_hex_decode(const char *hex, uint8_t **bytes_out, size_t *len_out);
+
+#endif /* K5_HEX_H */
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
index 58ac2e333..caaf15822 100644
--- a/src/util/support/Makefile.in
+++ b/src/util/support/Makefile.in
@@ -82,6 +82,7 @@ STLIBOBJS= \
path.o \
base64.o \
json.o \
+ hex.o \
bcmp.o \
strerror_r.o \
$(GETTIMEOFDAY_ST_OBJ) \
@@ -107,6 +108,7 @@ LIBOBJS= \
$(OUTPRE)path.$(OBJEXT) \
$(OUTPRE)base64.$(OBJEXT) \
$(OUTPRE)json.$(OBJEXT) \
+ $(OUTPRE)hex.$(OBJEXT) \
$(OUTPRE)bcmp.$(OBJEXT) \
$(OUTPRE)strerror_r.$(OBJEXT) \
$(GETTIMEOFDAY_OBJ) \
@@ -137,10 +139,12 @@ SRCS=\
$(srcdir)/t_unal.c \
$(srcdir)/t_path.c \
$(srcdir)/t_json.c \
+ $(srcdir)/t_hex.c \
$(srcdir)/zap.c \
$(srcdir)/path.c \
$(srcdir)/base64.c \
$(srcdir)/json.c \
+ $(srcdir)/hex.c \
$(srcdir)/bcmp.c \
$(srcdir)/strerror_r.c \
$(srcdir)/t_utf8.c \
@@ -216,6 +220,9 @@ T_JSON_OBJS= t_json.o json.o base64.o k5buf.o $(PRINTF_ST_OBJ)
t_json: $(T_JSON_OBJS)
$(CC_LINK) -o $@ $(T_JSON_OBJS)
+t_hex: t_hex.o hex.o
+ $(CC_LINK) -o $@ t_hex.o hex.o
+
t_unal: t_unal.o
$(CC_LINK) -o t_unal t_unal.o
@@ -227,7 +234,8 @@ T_UTF16_OBJS= t_utf16.o utf8_conv.o utf8.o k5buf.o $(PRINTF_ST_OBJ)
t_utf16: $(T_UTF16_OBJS)
$(CC_LINK) -o $@ $(T_UTF16_OBJS)
-TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_unal t_utf8 t_utf16
+TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_hex t_unal t_utf8 \
+ t_utf16
check-unix: $(TEST_PROGS)
./t_k5buf
@@ -235,6 +243,7 @@ check-unix: $(TEST_PROGS)
./t_path_win
./t_base64
./t_json
+ ./t_hex
./t_unal
./t_utf8
./t_utf16
@@ -242,8 +251,8 @@ check-unix: $(TEST_PROGS)
clean:
$(RM) t_k5buf.o t_k5buf t_unal.o t_unal path_win.o path_win
$(RM) t_path_win.o t_path_win t_path.o t_path t_base64.o t_base64
- $(RM) t_json.o t_json libkrb5support.exports t_utf8.o t_utf8
- $(RM) t_utf16.o t_utf16
+ $(RM) t_json.o t_json t_hex.o t_hex libkrb5support.exports
+ $(RM) t_utf8.o t_utf8 t_utf16.o t_utf16
@lib_frag@
@libobj_frag@
diff --git a/src/util/support/deps b/src/util/support/deps
index 34d8a884b..80e9a1c58 100644
--- a/src/util/support/deps
+++ b/src/util/support/deps
@@ -63,6 +63,9 @@ t_path.so t_path.po $(OUTPRE)t_path.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
t_path.c
t_json.so t_json.po $(OUTPRE)t_json.$(OBJEXT): $(top_srcdir)/include/k5-json.h \
t_json.c
+t_hex.so t_hex.po $(OUTPRE)t_hex.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h t_hex.c
zap.so zap.po $(OUTPRE)zap.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
zap.c
@@ -76,6 +79,9 @@ json.so json.po $(OUTPRE)json.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/k5-base64.h $(top_srcdir)/include/k5-buf.h \
$(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
$(top_srcdir)/include/k5-thread.h json.c
+hex.so hex.po $(OUTPRE)hex.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h hex.c
bcmp.so bcmp.po $(OUTPRE)bcmp.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
bcmp.c
diff --git a/src/util/support/hex.c b/src/util/support/hex.c
new file mode 100644
index 000000000..4407ff9ff
--- /dev/null
+++ b/src/util/support/hex.c
@@ -0,0 +1,116 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/hex.c - hex encoding/decoding implementation */
+/*
+ * Copyright (C) 2018 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <k5-platform.h>
+#include <k5-hex.h>
+#include <ctype.h>
+
+static inline char
+hex_digit(uint8_t bval, int uppercase)
+{
+ assert(bval >= 0 && bval <= 0xF);
+ if (bval < 10)
+ return '0' + bval;
+ else if (uppercase)
+ return 'A' + (bval - 10);
+ else
+ return 'a' + (bval - 10);
+}
+
+int
+k5_hex_encode(const void *bytes, size_t len, int uppercase, char **hex_out)
+{
+ size_t i;
+ const uint8_t *p = bytes;
+ char *hex;
+
+ *hex_out = NULL;
+
+ hex = malloc(len * 2 + 1);
+ if (hex == NULL)
+ return ENOMEM;
+
+ for (i = 0; i < len; i++) {
+ hex[i * 2] = hex_digit(p[i] >> 4, uppercase);
+ hex[i * 2 + 1] = hex_digit(p[i] & 0xF, uppercase);
+ }
+ hex[len * 2] = '\0';
+
+ *hex_out = hex;
+ return 0;
+}
+
+/* Decode a hex digit. Return 0-15 on success, -1 on invalid input. */
+static inline int
+decode_hexchar(unsigned char c)
+{
+ if (isdigit(c))
+ return c - '0';
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1;
+}
+
+int
+k5_hex_decode(const char *hex, uint8_t **bytes_out, size_t *len_out)
+{
+ size_t hexlen, i;
+ int h1, h2;
+ uint8_t *bytes;
+
+ *bytes_out = NULL;
+ *len_out = 0;
+
+ hexlen = strlen(hex);
+ if (hexlen % 2 != 0)
+ return EINVAL;
+ bytes = malloc(hexlen / 2 + 1);
+ if (bytes == NULL)
+ return ENOMEM;
+
+ for (i = 0; i < hexlen / 2; i++) {
+ h1 = decode_hexchar(hex[i * 2]);
+ h2 = decode_hexchar(hex[i * 2 + 1]);
+ if (h1 == -1 || h2 == -1) {
+ free(bytes);
+ return EINVAL;
+ }
+ bytes[i] = h1 * 16 + h2;
+ }
+ bytes[i] = 0;
+
+ *bytes_out = bytes;
+ *len_out = hexlen / 2;
+ return 0;
+}
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index fd74a1897..30c946e7e 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -16,6 +16,8 @@ k5_get_error
k5_free_error
k5_clear_error
k5_set_error_info_callout_fn
+k5_hex_decode
+k5_hex_encode
k5_json_array_add
k5_json_array_create
k5_json_array_fmt
diff --git a/src/util/support/t_hex.c b/src/util/support/t_hex.c
new file mode 100644
index 000000000..a586a1bc8
--- /dev/null
+++ b/src/util/support/t_hex.c
@@ -0,0 +1,169 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/t_hex.c - Test hex encoding and decoding */
+/*
+ * Copyright (C) 2018 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <k5-platform.h>
+#include <k5-hex.h>
+
+struct {
+ const char *hex;
+ const char *binary;
+ size_t binary_len;
+ int uppercase;
+} tests[] = {
+ /* Invalid hex strings */
+ { "1" },
+ { "123" },
+ { "0/" },
+ { "/0" },
+ { "0:" },
+ { ":0" },
+ { "0@" },
+ { "@0" },
+ { "0G" },
+ { "G0" },
+ { "0`" },
+ { "`0" },
+ { "0g" },
+ { "g0" },
+ { " 00 " },
+ { "0\x01" },
+
+ { "", "", 0 },
+ { "00", "\x00", 1 },
+ { "01", "\x01", 1 },
+ { "10", "\x10", 1 },
+ { "01ff", "\x01\xFF", 2 },
+ { "A0B0C0", "\xA0\xB0\xC0", 3, 1 },
+ { "1a2b3c4d5e6f", "\x1A\x2B\x3C\x4D\x5E\x6F", 6 },
+ { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 32 },
+
+ /* All byte values, lowercase */
+ { "0001020304050607", "\x00\x01\x02\x03\x04\x05\x06\x07", 8 },
+ { "08090a0b0c0d0e0f", "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8 },
+ { "1011121314151617", "\x10\x11\x12\x13\x14\x15\x16\x17", 8 },
+ { "18191a1b1c1d1e1f", "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8 },
+ { "2021222324252627", "\x20\x21\x22\x23\x24\x25\x26\x27", 8 },
+ { "28292a2b2c2d2e2f", "\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F", 8 },
+ { "3031323334353637", "\x30\x31\x32\x33\x34\x35\x36\x37", 8 },
+ { "38393a3b3c3d3e3f", "\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F", 8 },
+ { "4041424344454647", "\x40\x41\x42\x43\x44\x45\x46\x47", 8 },
+ { "48494a4b4c4d4e4f", "\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F", 8 },
+ { "5051525354555657", "\x50\x51\x52\x53\x54\x55\x56\x57", 8 },
+ { "58595a5b5c5d5e5f", "\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F", 8 },
+ { "6061626364656667", "\x60\x61\x62\x63\x64\x65\x66\x67", 8 },
+ { "68696a6b6c6d6e6f", "\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F", 8 },
+ { "7071727374757677", "\x70\x71\x72\x73\x74\x75\x76\x77", 8 },
+ { "78797a7b7c7d7e7f", "\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F", 8 },
+ { "8081828384858687", "\x80\x81\x82\x83\x84\x85\x86\x87", 8 },
+ { "88898a8b8c8d8e8f", "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F", 8 },
+ { "9091929394959697", "\x90\x91\x92\x93\x94\x95\x96\x97", 8 },
+ { "98999a9b9c9d9e9f", "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F", 8 },
+ { "a0a1a2a3a4a5a6a7", "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", 8 },
+ { "a8a9aaabacadaeaf", "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF", 8 },
+ { "b0b1b2b3b4b5b6b7", "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7", 8 },
+ { "b8b9babbbcbdbebf", "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", 8 },
+ { "c0c1c2c3c4c5c6c7", "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7", 8 },
+ { "c8c9cacbcccdcecf", "\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF", 8 },
+ { "d0d1d2d3d4d5d6d7", "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7", 8 },
+ { "d8d9dadbdcdddedf", "\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF", 8 },
+ { "e0e1e2e3e4e5e6e7", "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7", 8 },
+ { "e8e9eaebecedeeef", "\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF", 8 },
+ { "f0f1f2f3f4f5f6f7", "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7", 8 },
+ { "f8f9fafbfcfdfeff", "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", 8 },
+
+ /* All byte values, uppercase */
+ { "0001020304050607", "\x00\x01\x02\x03\x04\x05\x06\x07", 8, 1 },
+ { "08090A0B0C0D0E0F", "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8, 1 },
+ { "1011121314151617", "\x10\x11\x12\x13\x14\x15\x16\x17", 8, 1 },
+ { "18191A1B1C1D1E1F", "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8, 1 },
+ { "2021222324252627", "\x20\x21\x22\x23\x24\x25\x26\x27", 8, 1 },
+ { "28292A2B2C2D2E2F", "\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F", 8, 1 },
+ { "3031323334353637", "\x30\x31\x32\x33\x34\x35\x36\x37", 8, 1 },
+ { "38393A3B3C3D3E3F", "\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F", 8, 1 },
+ { "4041424344454647", "\x40\x41\x42\x43\x44\x45\x46\x47", 8, 1 },
+ { "48494A4B4C4D4E4F", "\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F", 8, 1 },
+ { "5051525354555657", "\x50\x51\x52\x53\x54\x55\x56\x57", 8, 1 },
+ { "58595A5B5C5D5E5F", "\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F", 8, 1 },
+ { "6061626364656667", "\x60\x61\x62\x63\x64\x65\x66\x67", 8, 1 },
+ { "68696A6B6C6D6E6F", "\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F", 8, 1 },
+ { "7071727374757677", "\x70\x71\x72\x73\x74\x75\x76\x77", 8, 1 },
+ { "78797A7B7C7D7E7F", "\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F", 8, 1 },
+ { "8081828384858687", "\x80\x81\x82\x83\x84\x85\x86\x87", 8, 1 },
+ { "88898A8B8C8D8E8F", "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F", 8, 1 },
+ { "9091929394959697", "\x90\x91\x92\x93\x94\x95\x96\x97", 8, 1 },
+ { "98999A9B9C9D9E9F", "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F", 8, 1 },
+ { "A0A1A2A3A4A5A6A7", "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", 8, 1 },
+ { "A8A9AAABACADAEAF", "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF", 8, 1 },
+ { "B0B1B2B3B4B5B6B7", "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7", 8, 1 },
+ { "B8B9BABBBCBDBEBF", "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", 8, 1 },
+ { "C0C1C2C3C4C5C6C7", "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7", 8, 1 },
+ { "C8C9CACBCCCDCECF", "\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF", 8, 1 },
+ { "D0D1D2D3D4D5D6D7", "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7", 8, 1 },
+ { "D8D9DADBDCDDDEDF", "\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF", 8, 1 },
+ { "E0E1E2E3E4E5E6E7", "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7", 8, 1 },
+ { "E8E9EAEBECEDEEEF", "\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF", 8, 1 },
+ { "F0F1F2F3F4F5F6F7", "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7", 8, 1 },
+ { "F8F9FAFBFCFDFEFF", "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", 8, 1 },
+};
+
+int main()
+{
+ size_t i;
+ char *hex;
+ int ret;
+ uint8_t *bytes;
+ size_t len;
+
+ for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
+ if (tests[i].binary == NULL) {
+ ret = k5_hex_decode(tests[i].hex, &bytes, &len);
+ assert(ret == EINVAL && bytes == NULL && len == 0);
+ continue;
+ }
+
+ ret = k5_hex_decode(tests[i].hex, &bytes, &len);
+ assert(ret == 0);
+ assert(len == tests[i].binary_len);
+ assert(memcmp(bytes, tests[i].binary, len) == 0);
+ assert(bytes[len] == 0);
+ free(bytes);
+
+ ret = k5_hex_encode((uint8_t *)tests[i].binary, tests[i].binary_len,
+ tests[i].uppercase, &hex);
+ assert(ret == 0);
+ assert(strcmp(tests[i].hex, hex) == 0);
+ free(hex);
+ }
+ return 0;
+}

View File

@ -0,0 +1,97 @@
From 3c47e4adbed5e0a2e7f3993a24097889216a9d50 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sat, 31 Oct 2020 17:07:05 -0400
Subject: [PATCH] Add recursion limit for ASN.1 indefinite lengths
The libkrb5 ASN.1 decoder supports BER indefinite lengths. It
computes the tag length using recursion; the lack of a recursion limit
allows an attacker to overrun the stack and cause the process to
crash. Reported by Demi Obenour.
CVE-2020-28196:
In MIT krb5 releases 1.11 and later, an unauthenticated attacker can
cause a denial of service for any client or server to which it can
send an ASN.1-encoded Kerberos message of sufficient length.
(cherry picked from commit 57415dda6cf04e73ffc3723be518eddfae599bfd)
ticket: 8959
version_fixed: 1.18.3
(cherry picked from commit 207ad69c87cf1b5c047d6c0c0165e5afe29700a6)
---
src/lib/krb5/asn.1/asn1_encode.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/src/lib/krb5/asn.1/asn1_encode.c b/src/lib/krb5/asn.1/asn1_encode.c
index a160cf4fe..cd6b879f7 100644
--- a/src/lib/krb5/asn.1/asn1_encode.c
+++ b/src/lib/krb5/asn.1/asn1_encode.c
@@ -356,7 +356,7 @@ make_tag(asn1buf *buf, const taginfo *t, size_t len)
static krb5_error_code
get_tag(const uint8_t *asn1, size_t len, taginfo *tag_out,
const uint8_t **contents_out, size_t *clen_out,
- const uint8_t **remainder_out, size_t *rlen_out)
+ const uint8_t **remainder_out, size_t *rlen_out, int recursion)
{
krb5_error_code ret;
uint8_t o;
@@ -394,9 +394,11 @@ get_tag(const uint8_t *asn1, size_t len, taginfo *tag_out,
/* Indefinite form (should not be present in DER, but we accept it). */
if (tag_out->construction != CONSTRUCTED)
return ASN1_MISMATCH_INDEF;
+ if (recursion >= 32)
+ return ASN1_OVERFLOW;
p = asn1;
while (!(len >= 2 && p[0] == 0 && p[1] == 0)) {
- ret = get_tag(p, len, &t, &c, &clen, &p, &len);
+ ret = get_tag(p, len, &t, &c, &clen, &p, &len, recursion + 1);
if (ret)
return ret;
}
@@ -613,7 +615,7 @@ split_der(asn1buf *buf, uint8_t *const *der, size_t len, taginfo *tag_out)
const uint8_t *contents, *remainder;
size_t clen, rlen;
- ret = get_tag(*der, len, tag_out, &contents, &clen, &remainder, &rlen);
+ ret = get_tag(*der, len, tag_out, &contents, &clen, &remainder, &rlen, 0);
if (ret)
return ret;
if (rlen != 0)
@@ -1199,7 +1201,7 @@ decode_atype(const taginfo *t, const uint8_t *asn1, size_t len,
const uint8_t *rem;
size_t rlen;
if (!tag->implicit) {
- ret = get_tag(asn1, len, &inner_tag, &asn1, &len, &rem, &rlen);
+ ret = get_tag(asn1, len, &inner_tag, &asn1, &len, &rem, &rlen, 0);
if (ret)
return ret;
/* Note: we don't check rlen (it should be 0). */
@@ -1420,7 +1422,7 @@ decode_sequence(const uint8_t *asn1, size_t len, const struct seq_info *seq,
for (i = 0; i < seq->n_fields; i++) {
if (len == 0)
break;
- ret = get_tag(asn1, len, &t, &contents, &clen, &asn1, &len);
+ ret = get_tag(asn1, len, &t, &contents, &clen, &asn1, &len, 0);
if (ret)
goto error;
/*
@@ -1478,7 +1480,7 @@ decode_sequence_of(const uint8_t *asn1, size_t len,
*seq_out = NULL;
*count_out = 0;
while (len > 0) {
- ret = get_tag(asn1, len, &t, &contents, &clen, &asn1, &len);
+ ret = get_tag(asn1, len, &t, &contents, &clen, &asn1, &len, 0);
if (ret)
goto error;
if (!check_atype_tag(elemtype, &t)) {
@@ -1584,7 +1586,7 @@ k5_asn1_full_decode(const krb5_data *code, const struct atype_info *a,
*retrep = NULL;
ret = get_tag((uint8_t *)code->data, code->length, &t, &contents,
- &clen, &remainder, &rlen);
+ &clen, &remainder, &rlen, 0);
if (ret)
return ret;
/* rlen should be 0, but we don't check it (and due to padding in

View File

@ -0,0 +1,226 @@
From 433dd85aaf8d9ed0e923c873f107995232b94422 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 26 Oct 2023 14:20:34 -0400
Subject: [PATCH] Add request_timeout configuration parameter
Add a parameter to limit the total amount of time taken for a KDC or
password change request.
ticket: 9106 (new)
(cherry picked from commit 802318cda963456b3ed7856c836e89da891483be)
---
doc/admin/conf_files/krb5_conf.rst | 9 ++++++
src/include/k5-int.h | 2 ++
src/lib/krb5/krb/init_ctx.c | 14 +++++++-
src/lib/krb5/os/sendto_kdc.c | 51 ++++++++++++++++++++----------
4 files changed, 58 insertions(+), 18 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 315253e378..557094f6a2 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -357,6 +357,15 @@ The libdefaults section may contain any of the following relations:
(:ref:`duration` string.) Sets the default renewable lifetime
for initial ticket requests. The default value is 0.
+**request_timeout**
+ (:ref:`duration` string.) Sets the maximum total time for KDC or
+ password change requests. This timeout does not affect the
+ intervals between requests, so setting a low timeout may result in
+ fewer requests being attempted and/or some servers not being
+ contacted. A value of 0 indicates no specific maximum, in which
+ case requests will time out if no server responds after several
+ tries. The default value is 0. (New in release 1.22.)
+
**spake_preauth_groups**
A whitespace or comma-separated list of words which specifies the
groups allowed for SPAKE preauthentication. The possible values
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 912aaedac4..9d5e41ca2c 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -293,6 +293,7 @@ typedef unsigned char u_char;
#define KRB5_CONF_SPAKE_PREAUTH_INDICATOR "spake_preauth_indicator"
#define KRB5_CONF_SPAKE_PREAUTH_KDC_CHALLENGE "spake_preauth_kdc_challenge"
#define KRB5_CONF_SPAKE_PREAUTH_GROUPS "spake_preauth_groups"
+#define KRB5_CONF_REQUEST_TIMEOUT "request_timeout"
#define KRB5_CONF_TICKET_LIFETIME "ticket_lifetime"
#define KRB5_CONF_UDP_PREFERENCE_LIMIT "udp_preference_limit"
#define KRB5_CONF_UNLOCKITER "unlockiter"
@@ -1218,6 +1219,7 @@ struct _krb5_context {
kdb5_dal_handle *dal_handle;
/* allowable clock skew */
krb5_deltat clockskew;
+ krb5_deltat req_timeout;
krb5_flags kdc_default_options;
krb5_flags library_options;
krb5_boolean profile_secure;
diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c
index 9a4741fa64..1a6e0bf672 100644
--- a/src/lib/krb5/krb/init_ctx.c
+++ b/src/lib/krb5/krb/init_ctx.c
@@ -163,7 +163,7 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
} seed_data;
krb5_data seed;
int tmp;
- char *plugin_dir = NULL;
+ char *plugin_dir = NULL, *timeout_str = NULL;
/* Verify some assumptions. If the assumptions hold and the
compiler is optimizing, this should result in no code being
@@ -257,6 +257,17 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
get_integer(ctx, KRB5_CONF_CLOCKSKEW, DEFAULT_CLOCKSKEW, &tmp);
ctx->clockskew = tmp;
+ retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS,
+ KRB5_CONF_REQUEST_TIMEOUT, NULL, NULL,
+ &timeout_str);
+ if (retval)
+ goto cleanup;
+ if (timeout_str != NULL) {
+ retval = krb5_string_to_deltat(timeout_str, &ctx->req_timeout);
+ if (retval)
+ goto cleanup;
+ }
+
get_integer(ctx, KRB5_CONF_KDC_DEFAULT_OPTIONS, KDC_OPT_RENEWABLE_OK,
&tmp);
ctx->kdc_default_options = tmp;
@@ -298,6 +309,7 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
cleanup:
profile_release_string(plugin_dir);
+ profile_release_string(timeout_str);
krb5_free_context(ctx);
return retval;
}
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 8e4fcd2a38..f57117126e 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -1390,34 +1390,41 @@ get_endtime(time_ms endtime, struct conn_state *conns)
static krb5_boolean
service_fds(krb5_context context, struct select_state *selstate,
- time_ms interval, struct conn_state *conns,
+ time_ms interval, time_ms timeout, struct conn_state *conns,
struct select_state *seltemp, const krb5_data *realm,
int (*msg_handler)(krb5_context, const krb5_data *, void *),
void *msg_handler_data, struct conn_state **winner_out)
{
int e, selret = 0;
- time_ms endtime;
+ time_ms curtime, interval_end, endtime;
struct conn_state *state;
*winner_out = NULL;
- e = get_curtime_ms(&endtime);
+ e = get_curtime_ms(&curtime);
if (e)
return TRUE;
- endtime += interval;
+ interval_end = curtime + interval;
e = 0;
while (selstate->nfds > 0) {
- e = cm_select_or_poll(selstate, get_endtime(endtime, conns),
- seltemp, &selret);
+ endtime = get_endtime(interval_end, conns);
+ /* Don't wait longer than the whole request should last. */
+ if (timeout && endtime > timeout)
+ endtime = timeout;
+ e = cm_select_or_poll(selstate, endtime, seltemp, &selret);
if (e == EINTR)
continue;
if (e != 0)
break;
- if (selret == 0)
- /* Timeout, return to caller. */
+ if (selret == 0) {
+ /* We timed out. Stop if we hit the overall request timeout. */
+ if (timeout && (get_curtime_ms(&curtime) || curtime >= timeout))
+ return TRUE;
+ /* Otherwise return to the caller to send the next request. */
return FALSE;
+ }
/* Got something on a socket, process it. */
for (state = conns; state != NULL; state = state->next) {
@@ -1490,7 +1497,7 @@ k5_sendto(krb5_context context, const krb5_data *message,
void *msg_handler_data)
{
int pass;
- time_ms delay;
+ time_ms delay, timeout = 0;
krb5_error_code retval;
struct conn_state *conns = NULL, *state, **tailptr, *next, *winner;
size_t s;
@@ -1500,6 +1507,13 @@ k5_sendto(krb5_context context, const krb5_data *message,
*reply = empty_data();
+ if (context->req_timeout) {
+ retval = get_curtime_ms(&timeout);
+ if (retval)
+ return retval;
+ timeout += 1000 * context->req_timeout;
+ }
+
/* One for use here, listing all our fds in use, and one for
* temporary use in service_fds, for the fds of interest. */
sel_state = malloc(2 * sizeof(*sel_state));
@@ -1527,8 +1541,9 @@ k5_sendto(krb5_context context, const krb5_data *message,
if (maybe_send(context, state, message, sel_state, realm,
callback_info))
continue;
- done = service_fds(context, sel_state, 1000, conns, seltemp,
- realm, msg_handler, msg_handler_data, &winner);
+ done = service_fds(context, sel_state, 1000, timeout, conns,
+ seltemp, realm, msg_handler, msg_handler_data,
+ &winner);
}
}
@@ -1540,13 +1555,13 @@ k5_sendto(krb5_context context, const krb5_data *message,
if (maybe_send(context, state, message, sel_state, realm,
callback_info))
continue;
- done = service_fds(context, sel_state, 1000, conns, seltemp,
+ done = service_fds(context, sel_state, 1000, timeout, conns, seltemp,
realm, msg_handler, msg_handler_data, &winner);
}
/* Wait for two seconds at the end of the first pass. */
if (!done) {
- done = service_fds(context, sel_state, 2000, conns, seltemp,
+ done = service_fds(context, sel_state, 2000, timeout, conns, seltemp,
realm, msg_handler, msg_handler_data, &winner);
}
@@ -1557,15 +1572,17 @@ k5_sendto(krb5_context context, const krb5_data *message,
if (maybe_send(context, state, message, sel_state, realm,
callback_info))
continue;
- done = service_fds(context, sel_state, 1000, conns, seltemp,
- realm, msg_handler, msg_handler_data, &winner);
+ done = service_fds(context, sel_state, 1000, timeout, conns,
+ seltemp, realm, msg_handler, msg_handler_data,
+ &winner);
if (sel_state->nfds == 0)
break;
}
/* Wait for the delay backoff at the end of this pass. */
if (!done) {
- done = service_fds(context, sel_state, delay, conns, seltemp,
- realm, msg_handler, msg_handler_data, &winner);
+ done = service_fds(context, sel_state, delay, timeout, conns,
+ seltemp, realm, msg_handler, msg_handler_data,
+ &winner);
}
if (sel_state->nfds == 0)
break;
--
2.44.0

View File

@ -0,0 +1,303 @@
From bb5552ece2a351dc3ccab52cceea1eaffeacd768 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 14 Dec 2020 13:16:17 -0500
Subject: [PATCH] Add support for start_realm cache config
When making TGS requests, if start_realm is set in the cache, use the
named realm to look up the initial TGT for referral or cross-realm
requests. (Also correct a comment in struct _tkt_creds_context: the
ccache field is an owner pointer, not an alias.)
Add an internal API k5_cc_store_primary_cred(), which sets start_realm
if the cred being stored is a TGT for a realm other than the client
realm. Use this API when acquiring initial tickets with a
caller-specified output ccache, when renewing or validating tickets
with kinit, when accepting a delegated credential in a GSS context,
and when storing a single cred with kvno --out-cache.
ticket: 8332
tags: pullup
target_version: 1.19
(cherry picked from commit 0d56740ab9fcc40dc7f46c6fbebdf8f1214f9d96)
[rharwood@redhat.com: backport around spelling and canonicalization fallback]
---
doc/formats/ccache_file_format.rst | 6 +++++
src/clients/kinit/kinit.c | 2 +-
src/clients/kvno/kvno.c | 5 ++++-
src/include/k5-int.h | 4 ++++
src/lib/gssapi/krb5/accept_sec_context.c | 2 +-
src/lib/krb5/ccache/ccfns.c | 20 +++++++++++++++++
src/lib/krb5/krb/get_creds.c | 28 ++++++++++++++++++------
src/lib/krb5/krb/get_in_tkt.c | 2 +-
src/lib/krb5/libkrb5.exports | 1 +
src/lib/krb5_32.def | 3 +++
src/tests/t_crossrealm.py | 8 +++++++
src/tests/t_pkinit.py | 3 +++
12 files changed, 73 insertions(+), 11 deletions(-)
diff --git a/doc/formats/ccache_file_format.rst b/doc/formats/ccache_file_format.rst
index 6349e0d29..6138c1b58 100644
--- a/doc/formats/ccache_file_format.rst
+++ b/doc/formats/ccache_file_format.rst
@@ -174,3 +174,9 @@ refresh_time
decimal representation of a timestamp at which the GSS mechanism
should attempt to refresh the credential cache from the client
keytab.
+
+start_realm
+ This key indicates the realm of the ticket-granting ticket to be
+ used for TGS requests, when making a referrals request or
+ beginning a cross-realm request. If it is not present, the client
+ realm is used.
diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c
index 3fdae2878..e5ebeb895 100644
--- a/src/clients/kinit/kinit.c
+++ b/src/clients/kinit/kinit.c
@@ -828,7 +828,7 @@ k5_kinit(struct k_opts *opts, struct k5_data *k5)
if (opts->verbose)
fprintf(stderr, _("Initialized cache\n"));
- ret = krb5_cc_store_cred(k5->ctx, k5->out_cc, &my_creds);
+ ret = k5_cc_store_primary_cred(k5->ctx, k5->out_cc, &my_creds);
if (ret) {
com_err(progname, ret, _("while storing credentials"));
goto cleanup;
diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c
index c5f6bf700..f83c68a99 100644
--- a/src/clients/kvno/kvno.c
+++ b/src/clients/kvno/kvno.c
@@ -561,7 +561,10 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr,
}
initialized = 1;
}
- ret = krb5_cc_store_cred(context, out_ccache, creds);
+ if (count == 1)
+ ret = k5_cc_store_primary_cred(context, out_ccache, creds);
+ else
+ ret = krb5_cc_store_cred(context, out_ccache, creds);
if (ret) {
com_err(prog, ret, _("while storing creds in output ccache"));
exit(1);
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index eb18a4cd6..912aaedac 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -307,6 +307,7 @@ typedef unsigned char u_char;
#define KRB5_CC_CONF_PA_TYPE "pa_type"
#define KRB5_CC_CONF_PROXY_IMPERSONATOR "proxy_impersonator"
#define KRB5_CC_CONF_REFRESH_TIME "refresh_time"
+#define KRB5_CC_CONF_START_REALM "start_realm"
/* Error codes used in KRB_ERROR protocol messages.
Return values of library routines are based on a different error table
@@ -1910,6 +1911,9 @@ krb5_ser_unpack_bytes(krb5_octet *, size_t, krb5_octet **, size_t *);
krb5_error_code KRB5_CALLCONV
krb5int_cc_default(krb5_context, krb5_ccache *);
+krb5_error_code
+k5_cc_store_primary_cred(krb5_context, krb5_ccache, krb5_creds *);
+
/* Fill in the buffer with random alpha-numeric data. */
krb5_error_code
krb5int_random_string(krb5_context, char *string, unsigned int length);
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index 3d5b84b15..abccb5d11 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -216,7 +216,7 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client)))
goto cleanup;
- if ((retval = krb5_cc_store_cred(context, ccache, creds[0])))
+ if ((retval = k5_cc_store_primary_cred(context, ccache, creds[0])))
goto cleanup;
/* generate a delegated credential handle */
diff --git a/src/lib/krb5/ccache/ccfns.c b/src/lib/krb5/ccache/ccfns.c
index 62a6983d8..23edc2578 100644
--- a/src/lib/krb5/ccache/ccfns.c
+++ b/src/lib/krb5/ccache/ccfns.c
@@ -297,3 +297,23 @@ krb5_cc_switch(krb5_context context, krb5_ccache cache)
return 0;
return cache->ops->switch_to(context, cache);
}
+
+krb5_error_code
+k5_cc_store_primary_cred(krb5_context context, krb5_ccache cache,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+
+ /* Write a start realm if we're writing a TGT and the client realm isn't
+ * the same as the TGS realm. */
+ if (IS_TGS_PRINC(creds->server) &&
+ !data_eq(creds->client->realm, creds->server->data[1])) {
+ ret = krb5_cc_set_config(context, cache, NULL,
+ KRB5_CC_CONF_START_REALM,
+ &creds->server->data[1]);
+ if (ret)
+ return ret;
+ }
+
+ return krb5_cc_store_cred(context, cache, creds);
+}
diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
index e0a3b5cd8..b40f705fc 100644
--- a/src/lib/krb5/krb/get_creds.c
+++ b/src/lib/krb5/krb/get_creds.c
@@ -149,7 +149,8 @@ struct _krb5_tkt_creds_context {
krb5_principal client; /* Caller-requested client principal (alias) */
krb5_principal server; /* Server principal (alias) */
krb5_principal req_server; /* Caller-requested server principal */
- krb5_ccache ccache; /* Caller-provided ccache (alias) */
+ krb5_ccache ccache; /* Caller-provided ccache */
+ krb5_data start_realm; /* Realm of starting TGT in ccache */
krb5_flags req_options; /* Caller-requested KRB5_GC_* options */
krb5_flags req_kdcopt; /* Caller-requested options as KDC options */
krb5_authdata **authdata; /* Caller-requested authdata */
@@ -783,7 +784,7 @@ get_cached_local_tgt(krb5_context context, krb5_tkt_creds_context ctx,
return code;
/* Construct the principal name. */
- code = krb5int_tgtname(context, &ctx->client->realm, &ctx->client->realm,
+ code = krb5int_tgtname(context, &ctx->start_realm, &ctx->start_realm,
&tgtname);
if (code != 0)
return code;
@@ -821,7 +822,7 @@ init_realm_path(krb5_context context, krb5_tkt_creds_context ctx)
size_t nrealms;
/* Get the client realm path and count its length. */
- code = k5_client_realm_path(context, &ctx->client->realm,
+ code = k5_client_realm_path(context, &ctx->start_realm,
&ctx->server->realm, &realm_path);
if (code != 0)
return code;
@@ -933,7 +934,7 @@ step_get_tgt(krb5_context context, krb5_tkt_creds_context ctx)
ctx->cur_realm = path_realm;
ctx->next_realm = ctx->last_realm;
}
- } else if (data_eq(*tgt_realm, ctx->client->realm)) {
+ } else if (data_eq(*tgt_realm, ctx->start_realm)) {
/* We were referred back to the local realm, which is bad. */
return KRB5_KDCREP_MODIFIED;
} else {
@@ -963,7 +964,7 @@ begin_get_tgt(krb5_context context, krb5_tkt_creds_context ctx)
ctx->state = STATE_GET_TGT;
- is_local_service = data_eq(ctx->client->realm, ctx->server->realm);
+ is_local_service = data_eq(ctx->start_realm, ctx->server->realm);
if (!is_local_service) {
/* See if we have a cached TGT for the server realm. */
code = get_cached_tgt(context, ctx, &ctx->server->realm, &cached_tgt);
@@ -1048,10 +1049,10 @@ begin(krb5_context context, krb5_tkt_creds_context ctx)
if (code != 0 || ctx->state == STATE_COMPLETE)
return code;
- /* If the server realm is unspecified, start with the client realm. */
+ /* If the server realm is unspecified, start with the TGT realm. */
if (krb5_is_referral_realm(&ctx->server->realm)) {
krb5_free_data_contents(context, &ctx->server->realm);
- code = krb5int_copy_data_contents(context, &ctx->client->realm,
+ code = krb5int_copy_data_contents(context, &ctx->start_realm,
&ctx->server->realm);
TRACE_TKT_CREDS_REFERRAL_REALM(context, ctx->server);
if (code != 0)
@@ -1100,6 +1101,18 @@ krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache,
code = krb5_cc_dup(context, ccache, &ctx->ccache);
if (code != 0)
goto cleanup;
+
+ /* Get the start realm from the cache config, defaulting to the client
+ * realm. */
+ code = krb5_cc_get_config(context, ccache, NULL, "start_realm",
+ &ctx->start_realm);
+ if (code != 0) {
+ code = krb5int_copy_data_contents(context, &ctx->client->realm,
+ &ctx->start_realm);
+ if (code != 0)
+ goto cleanup;
+ }
+
code = krb5_copy_authdata(context, in_creds->authdata, &ctx->authdata);
if (code != 0)
goto cleanup;
@@ -1139,6 +1152,7 @@ krb5_tkt_creds_free(krb5_context context, krb5_tkt_creds_context ctx)
krb5int_fast_free_state(context, ctx->fast_state);
krb5_free_creds(context, ctx->in_creds);
krb5_cc_close(context, ctx->ccache);
+ krb5_free_data_contents(context, &ctx->start_realm);
krb5_free_principal(context, ctx->req_server);
krb5_free_authdata(context, ctx->authdata);
krb5_free_creds(context, ctx->cur_tgt);
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index cc0f70e83..f5dd7518b 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -1779,7 +1779,7 @@ init_creds_step_reply(krb5_context context,
code = krb5_cc_initialize(context, out_ccache, ctx->cred.client);
if (code != 0)
goto cc_cleanup;
- code = krb5_cc_store_cred(context, out_ccache, &ctx->cred);
+ code = k5_cc_store_primary_cred(context, out_ccache, &ctx->cred);
if (code != 0)
goto cc_cleanup;
if (fast_avail) {
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 5aba29ee4..cab5b3b17 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -125,6 +125,7 @@ k5_add_pa_data_from_data
k5_alloc_pa_data
k5_authind_decode
k5_build_conf_principals
+k5_cc_store_primary_cred
k5_ccselect_free_context
k5_change_error_message_code
k5_etypes_contains
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
index a0734c729..de5823c17 100644
--- a/src/lib/krb5_32.def
+++ b/src/lib/krb5_32.def
@@ -499,3 +499,6 @@ EXPORTS
k5_size_context @467 ; PRIVATE GSSAPI
k5_size_keyblock @468 ; PRIVATE GSSAPI
k5_size_principal @469 ; PRIVATE GSSAPI
+
+; new in 1.19
+ k5_cc_store_primary_cred @470 ; PRIVATE
diff --git a/src/tests/t_crossrealm.py b/src/tests/t_crossrealm.py
index fa7fd2604..28b397cfb 100755
--- a/src/tests/t_crossrealm.py
+++ b/src/tests/t_crossrealm.py
@@ -77,6 +77,14 @@ r1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)),
{'realm': 'B.X'}))
test_kvno(r1, r3.host_princ, 'KDC domain walk')
check_klist(r1, (tgt(r1, r1), r3.host_princ))
+
+# Test start_realm in this setup.
+r1.run([kvno, '--out-cache', r1.ccache, r2.krbtgt_princ])
+r1.run([klist, '-C'], expected_msg='config: start_realm = X')
+msgs = ('Requesting TGT krbtgt/B.X@X using TGT krbtgt/X@X',
+ 'Received TGT for service realm: krbtgt/B.X@X')
+r1.run([kvno, r3.host_princ], expected_trace=msgs)
+
stop(r1, r2, r3)
# Test client capaths. The client in A will ask for a cross TGT to D,
diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py
index ecd450e8a..f224383c8 100755
--- a/src/tests/t_pkinit.py
+++ b/src/tests/t_pkinit.py
@@ -130,6 +130,9 @@ realm.run([kvno, realm.host_princ])
out = realm.run(['./adata', realm.host_princ])
if '97:' in out:
fail('auth indicators seen in anonymous PKINIT ticket')
+# Verify start_realm setting and test referrals TGS request.
+realm.run([klist, '-C'], expected_msg='start_realm = KRBTEST.COM')
+realm.run([kvno, '-S', 'host', hostname])
# Test anonymous kadmin.
mark('anonymous kadmin')

View File

@ -0,0 +1,403 @@
From a1f38973435b60c7f147abfca12b95c6a0a64406 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 17 Jun 2020 20:48:38 -0400
Subject: [PATCH] Add three kvno options from Heimdal kgetcred
Add the flags --cached-only and --no-store, which pass the
corresponding options to krb5_get_credentials(). Add the option
--out-cache to write the retrieved credentials to a specified output
cache.
Add a Python test script for kvno command-line options, including
tests for the new options.
ticket: 8917 (new)
---
doc/user/user_commands/kvno.rst | 13 ++++
src/clients/kvno/Makefile.in | 3 +
src/clients/kvno/kvno.c | 115 +++++++++++++++++++++++---------
src/clients/kvno/t_kvno.py | 75 +++++++++++++++++++++
src/man/kvno.man | 13 ++++
5 files changed, 187 insertions(+), 32 deletions(-)
create mode 100644 src/clients/kvno/t_kvno.py
diff --git a/doc/user/user_commands/kvno.rst b/doc/user/user_commands/kvno.rst
index 3892f0ca5..718313576 100644
--- a/doc/user/user_commands/kvno.rst
+++ b/doc/user/user_commands/kvno.rst
@@ -74,6 +74,19 @@ OPTIONS
client principal with the X.509 certificate in *cert_file*. The
certificate file must be in PEM format.
+**--cached-only**
+ Only retrieve credentials already present in the cache, not from
+ the KDC.
+
+**--no-store**
+ Do not store retrieved credentials in the cache. If
+ **--out-cache** is also specified, credentials will still be
+ stored into the output credential cache.
+
+**--out-cache** *ccache*
+ Initialize *ccache* and store all retrieved credentials into it.
+ Do not store acquired credentials in the input cache.
+
**--u2u** *ccache*
Requests a user-to-user ticket. *ccache* must contain a local
krbtgt ticket for the server principal. The reported version
diff --git a/src/clients/kvno/Makefile.in b/src/clients/kvno/Makefile.in
index 1c3f79392..5ba877271 100644
--- a/src/clients/kvno/Makefile.in
+++ b/src/clients/kvno/Makefile.in
@@ -26,6 +26,9 @@ kvno: kvno.o $(KRB5_BASE_DEPLIBS)
##WIN32## link $(EXE_LINKOPTS) /out:$@ $**
##WIN32## $(_VC_MANIFEST_EMBED_EXE)
+check-pytests: kvno
+ $(RUNPYTEST) $(srcdir)/t_kvno.py $(PYTESTFLAGS)
+
clean-unix::
$(RM) kvno.o kvno
diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c
index 2472c0cfe..9d85864f6 100644
--- a/src/clients/kvno/kvno.c
+++ b/src/clients/kvno/kvno.c
@@ -44,14 +44,17 @@ xusage()
fprintf(stderr, _("usage: %s [-C] [-u] [-c ccache] [-e etype]\n"), prog);
fprintf(stderr, _("\t[-k keytab] [-S sname] [{-I | -U} for_user | "
"[-F cert_file] [-P]]\n"));
- fprintf(stderr, _("\t[--u2u ccache] service1 service2 ...\n"));
+ fprintf(stderr, _("\t[--cached-only] [--no-store] [--out-cache ccache] "
+ "[--u2u ccache]\n"));
+ fprintf(stderr, _("\tservice1 service2 ...\n"));
exit(1);
}
static void do_v5_kvno(int argc, char *argv[], char *ccachestr, char *etypestr,
- char *keytab_name, char *sname, int canon, int unknown,
- char *for_user, int for_user_enterprise,
- char *for_user_cert_file, int proxy,
+ char *keytab_name, char *sname, int cached_only,
+ int canon, int no_store, int unknown, char *for_user,
+ int for_user_enterprise, char *for_user_cert_file,
+ int proxy, const char *out_ccname,
const char *u2u_ccname);
#include <com_err.h>
@@ -61,18 +64,21 @@ static void extended_com_err_fn(const char *myprog, errcode_t code,
int
main(int argc, char *argv[])
{
- enum { OPTION_U2U = 256 };
- struct option lopts[] = {
- { "u2u", 1, NULL, OPTION_U2U },
- { NULL, 0, NULL, 0 }
- };
+ enum { OPTION_U2U = 256, OPTION_OUT_CACHE = 257 };
const char *shopts = "uCc:e:hk:qPS:I:U:F:";
int option;
char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL;
char *sname = NULL, *for_user = NULL, *u2u_ccname = NULL;
- char *for_user_cert_file = NULL;
+ char *for_user_cert_file = NULL, *out_ccname = NULL;
int canon = 0, unknown = 0, proxy = 0, for_user_enterprise = 0;
- int impersonate = 0;
+ int impersonate = 0, cached_only = 0, no_store = 0;
+ struct option lopts[] = {
+ { "cached-only", 0, &cached_only, 1 },
+ { "no-store", 0, &no_store, 1 },
+ { "out-cache", 1, NULL, OPTION_OUT_CACHE },
+ { "u2u", 1, NULL, OPTION_U2U },
+ { NULL, 0, NULL, 0 }
+ };
setlocale(LC_ALL, "");
set_com_err_hook(extended_com_err_fn);
@@ -135,6 +141,12 @@ main(int argc, char *argv[])
case OPTION_U2U:
u2u_ccname = optarg;
break;
+ case OPTION_OUT_CACHE:
+ out_ccname = optarg;
+ break;
+ case 0:
+ /* If this option set a flag, do nothing else now. */
+ break;
default:
xusage();
break;
@@ -159,8 +171,9 @@ main(int argc, char *argv[])
xusage();
do_v5_kvno(argc - optind, argv + optind, ccachestr, etypestr, keytab_name,
- sname, canon, unknown, for_user, for_user_enterprise,
- for_user_cert_file, proxy, u2u_ccname);
+ sname, cached_only, canon, no_store, unknown, for_user,
+ for_user_enterprise, for_user_cert_file, proxy, out_ccname,
+ u2u_ccname);
return 0;
}
@@ -274,14 +287,16 @@ static krb5_error_code
kvno(const char *name, krb5_ccache ccache, krb5_principal me,
krb5_enctype etype, krb5_keytab keytab, const char *sname,
krb5_flags options, int unknown, krb5_principal for_user_princ,
- krb5_data *for_user_cert, int proxy, krb5_data *u2u_ticket)
+ krb5_data *for_user_cert, int proxy, krb5_data *u2u_ticket,
+ krb5_creds **creds_out)
{
krb5_error_code ret;
krb5_principal server = NULL;
krb5_ticket *ticket = NULL;
- krb5_creds in_creds, *out_creds = NULL;
+ krb5_creds in_creds, *creds = NULL;
char *princ = NULL;
+ *creds_out = NULL;
memset(&in_creds, 0, sizeof(in_creds));
if (sname != NULL) {
@@ -321,13 +336,12 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me,
in_creds.client = for_user_princ;
in_creds.server = me;
ret = krb5_get_credentials_for_user(context, options, ccache,
- &in_creds, for_user_cert,
- &out_creds);
+ &in_creds, for_user_cert, &creds);
} else {
in_creds.client = me;
in_creds.server = server;
ret = krb5_get_credentials(context, options, ccache, &in_creds,
- &out_creds);
+ &creds);
}
if (ret) {
@@ -336,7 +350,7 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me,
}
/* We need a native ticket. */
- ret = krb5_decode_ticket(&out_creds->ticket, &ticket);
+ ret = krb5_decode_ticket(&creds->ticket, &ticket);
if (ret) {
com_err(prog, ret, _("while decoding ticket for %s"), princ);
goto cleanup;
@@ -362,15 +376,15 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me,
}
if (proxy) {
- in_creds.client = out_creds->client;
- out_creds->client = NULL;
- krb5_free_creds(context, out_creds);
- out_creds = NULL;
+ in_creds.client = creds->client;
+ creds->client = NULL;
+ krb5_free_creds(context, creds);
+ creds = NULL;
in_creds.server = server;
ret = krb5_get_credentials_for_proxy(context, KRB5_GC_CANONICALIZE,
ccache, &in_creds, ticket,
- &out_creds);
+ &creds);
krb5_free_principal(context, in_creds.client);
if (ret) {
com_err(prog, ret, _("%s: constrained delegation failed"),
@@ -379,10 +393,13 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me,
}
}
+ *creds_out = creds;
+ creds = NULL;
+
cleanup:
krb5_free_principal(context, server);
krb5_free_ticket(context, ticket);
- krb5_free_creds(context, out_creds);
+ krb5_free_creds(context, creds);
krb5_free_unparsed_name(context, princ);
return ret;
}
@@ -428,19 +445,28 @@ cleanup:
static void
do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr,
- char *keytab_name, char *sname, int canon, int unknown,
- char *for_user, int for_user_enterprise,
- char *for_user_cert_file, int proxy, const char *u2u_ccname)
+ char *keytab_name, char *sname, int cached_only, int canon,
+ int no_store, int unknown, char *for_user, int for_user_enterprise,
+ char *for_user_cert_file, int proxy, const char *out_ccname,
+ const char *u2u_ccname)
{
krb5_error_code ret;
- int i, errors, flags;
+ int i, errors, flags, initialized = 0;
krb5_enctype etype;
- krb5_ccache ccache;
+ krb5_ccache ccache, out_ccache = NULL;
krb5_principal me;
krb5_keytab keytab = NULL;
krb5_principal for_user_princ = NULL;
- krb5_flags options = canon ? KRB5_GC_CANONICALIZE : 0;
+ krb5_flags options = 0;
krb5_data cert_data = empty_data(), *user_cert = NULL, *u2u_ticket = NULL;
+ krb5_creds *creds;
+
+ if (canon)
+ options |= KRB5_GC_CANONICALIZE;
+ if (cached_only)
+ options |= KRB5_GC_CACHED;
+ if (no_store || out_ccname != NULL)
+ options |= KRB5_GC_NO_STORE;
ret = krb5_init_context(&context);
if (ret) {
@@ -467,6 +493,14 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr,
exit(1);
}
+ if (out_ccname != NULL) {
+ ret = krb5_cc_resolve(context, out_ccname, &out_ccache);
+ if (ret) {
+ com_err(prog, ret, _("while resolving output ccache"));
+ exit(1);
+ }
+ }
+
if (keytab_name != NULL) {
ret = krb5_kt_resolve(context, keytab_name, &keytab);
if (ret) {
@@ -513,8 +547,25 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr,
errors = 0;
for (i = 0; i < count; i++) {
if (kvno(names[i], ccache, me, etype, keytab, sname, options, unknown,
- for_user_princ, user_cert, proxy, u2u_ticket) != 0)
+ for_user_princ, user_cert, proxy, u2u_ticket, &creds) != 0) {
errors++;
+ } else if (out_ccache != NULL) {
+ if (!initialized) {
+ ret = krb5_cc_initialize(context, out_ccache, creds->client);
+ if (ret) {
+ com_err(prog, ret, _("while initializing output ccache"));
+ exit(1);
+ }
+ initialized = 1;
+ }
+ ret = krb5_cc_store_cred(context, out_ccache, creds);
+ if (ret) {
+ com_err(prog, ret, _("while storing creds in output ccache"));
+ exit(1);
+ }
+ }
+
+ krb5_free_creds(context, creds);
}
if (keytab != NULL)
diff --git a/src/clients/kvno/t_kvno.py b/src/clients/kvno/t_kvno.py
new file mode 100644
index 000000000..e98b90e8a
--- /dev/null
+++ b/src/clients/kvno/t_kvno.py
@@ -0,0 +1,75 @@
+from k5test import *
+
+realm = K5Realm()
+
+def check_cache(ccache, expected_services):
+ # Fetch the klist output and skip past the header.
+ lines = realm.run([klist, '-c', ccache]).splitlines()
+ lines = lines[4:]
+
+ # For each line not beginning with an indent, match against the
+ # expected service principals.
+ svcs = {x: True for x in expected_services}
+ for l in lines:
+ if not l.startswith('\t'):
+ svcprinc = l.split()[4]
+ if svcprinc in svcs:
+ del svcs[svcprinc]
+ else:
+ fail('unexpected service princ ' + svcprinc)
+
+ if svcs:
+ fail('services not found in klist output: ' + ' '.join(svcs.keys()))
+
+
+mark('no options')
+realm.run([kvno, realm.user_princ], expected_msg='user@KRBTEST.COM: kvno = 1')
+check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ])
+
+mark('-e')
+msgs = ('etypes requested in TGS request: camellia128-cts',
+ '/KDC has no support for encryption type')
+realm.run([kvno, '-e', 'camellia128-cts', realm.host_princ],
+ expected_code=1, expected_trace=msgs)
+
+mark('--cached-only')
+realm.run([kvno, '--cached-only', realm.user_princ], expected_msg='kvno = 1')
+realm.run([kvno, '--cached-only', realm.host_princ],
+ expected_code=1, expected_msg='Matching credential not found')
+check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ])
+
+mark('--no-store')
+realm.run([kvno, '--no-store', realm.host_princ], expected_msg='kvno = 1')
+check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ])
+
+mark('--out-cache') # and multiple services
+out_ccache = os.path.join(realm.testdir, 'ccache.out')
+realm.run([kvno, '--out-cache', out_ccache,
+ realm.host_princ, realm.admin_princ])
+check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ])
+check_cache(out_ccache, [realm.host_princ, realm.admin_princ])
+
+mark('--out-cache --cached-only') # tests out-cache overwriting, and -q
+realm.run([kvno, '--out-cache', out_ccache, '--cached-only', realm.host_princ],
+ expected_code=1, expected_msg='Matching credential not found')
+out = realm.run([kvno, '-q', '--out-cache', out_ccache, '--cached-only',
+ realm.user_princ])
+if out:
+ fail('unexpected kvno output with -q')
+check_cache(out_ccache, [realm.user_princ])
+
+mark('-U') # and -c
+svc_ccache = os.path.join(realm.testdir, 'ccache.svc')
+realm.run([kinit, '-k', '-c', svc_ccache, realm.host_princ])
+realm.run([kvno, '-c', svc_ccache, '-U', 'user', realm.host_princ])
+realm.run([klist, '-c', svc_ccache], expected_msg='for client user@')
+realm.run([kvno, '-c', svc_ccache, '-U', 'user', '--out-cache', out_ccache,
+ realm.host_princ])
+out = realm.run([klist, '-c', out_ccache])
+if ('Default principal: user@KRBTEST.COM' not in out):
+ fail('wrong default principal in klist output')
+
+# More S4U options are tested in tests/gssapi/t_s4u.py.
+# --u2u is tested in tests/t_u2u.py.
+
+success('kvno tests')
diff --git a/src/man/kvno.man b/src/man/kvno.man
index 005a2ec97..b9f6739eb 100644
--- a/src/man/kvno.man
+++ b/src/man/kvno.man
@@ -95,6 +95,19 @@ Specifies that protocol transition is to be used, identifying the
client principal with the X.509 certificate in \fIcert_file\fP\&. The
certificate file must be in PEM format.
.TP
+\fB\-\-cached\-only\fP
+Only retrieve credentials already present in the cache, not from
+the KDC.
+.TP
+\fB\-\-no\-store\fP
+Do not store retrieved credentials in the cache. If
+\fB\-\-out\-cache\fP is also specified, credentials will still be
+stored into the output credential cache.
+.TP
+\fB\-\-out\-cache\fP \fIccache\fP
+Initialize \fIccache\fP and store all retrieved credentials into it.
+Do not store acquired credentials in the input cache.
+.TP
\fB\-\-u2u\fP \fIccache\fP
Requests a user\-to\-user ticket. \fIccache\fP must contain a local
krbtgt ticket for the server principal. The reported version

View File

@ -1,106 +0,0 @@
From f8b14b92cc4c82578f8fc56dd1fddebe88120769 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sat, 3 Feb 2018 20:53:42 -0500
Subject: [PATCH] Add vector support to k5_sha256()
Add a length argument so that multiple krb5_data values can be passed
to k5_sha256(), for efficient computation of SHA-256 hashes over
concatenations of data values.
(cherry picked from commit 4f3373e8c55b3e9bdfb5b065e07214c5816c85fa)
---
src/include/k5-int.h | 4 ++--
src/lib/crypto/builtin/sha2/sha256.c | 6 ++++--
src/lib/crypto/crypto_tests/t_sha2.c | 2 +-
src/lib/crypto/openssl/sha256.c | 6 ++++--
src/lib/krb5/rcache/rc_conv.c | 2 +-
5 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 9378ae047..1c1d9783b 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -635,9 +635,9 @@ krb5int_arcfour_gsscrypt(const krb5_keyblock *keyblock, krb5_keyusage usage,
#define K5_SHA256_HASHLEN (256 / 8)
-/* Write the SHA-256 hash of in to out. */
+/* Write the SHA-256 hash of in (containing n elements) to out. */
krb5_error_code
-k5_sha256(const krb5_data *in, uint8_t out[K5_SHA256_HASHLEN]);
+k5_sha256(const krb5_data *in, size_t n, uint8_t out[K5_SHA256_HASHLEN]);
/*
* Attempt to zero memory in a way that compilers won't optimize out.
diff --git a/src/lib/crypto/builtin/sha2/sha256.c b/src/lib/crypto/builtin/sha2/sha256.c
index 2b5cbe480..9a940b3f8 100644
--- a/src/lib/crypto/builtin/sha2/sha256.c
+++ b/src/lib/crypto/builtin/sha2/sha256.c
@@ -257,12 +257,14 @@ k5_sha256_final(void *res, SHA256_CTX *m)
}
krb5_error_code
-k5_sha256(const krb5_data *in, uint8_t out[K5_SHA256_HASHLEN])
+k5_sha256(const krb5_data *in, size_t n, uint8_t out[K5_SHA256_HASHLEN])
{
SHA256_CTX ctx;
+ size_t i;
k5_sha256_init(&ctx);
- k5_sha256_update(&ctx, in->data, in->length);
+ for (i = 0; i < n; i++)
+ k5_sha256_update(&ctx, in[i].data, in[i].length);
k5_sha256_final(out, &ctx);
return 0;
}
diff --git a/src/lib/crypto/crypto_tests/t_sha2.c b/src/lib/crypto/crypto_tests/t_sha2.c
index 12f32869b..e6fa58498 100644
--- a/src/lib/crypto/crypto_tests/t_sha2.c
+++ b/src/lib/crypto/crypto_tests/t_sha2.c
@@ -125,7 +125,7 @@ hash_test(const struct krb5_hash_provider *hash, struct test *tests)
if (hash == &krb5int_hash_sha256) {
/* Try again using k5_sha256(). */
- if (k5_sha256(&iov.data, (uint8_t *)hval.data) != 0)
+ if (k5_sha256(&iov.data, 1, (uint8_t *)hval.data) != 0)
abort();
if (memcmp(hval.data, t->hash, hval.length) != 0)
abort();
diff --git a/src/lib/crypto/openssl/sha256.c b/src/lib/crypto/openssl/sha256.c
index fa095d472..0edd8b7ba 100644
--- a/src/lib/crypto/openssl/sha256.c
+++ b/src/lib/crypto/openssl/sha256.c
@@ -34,16 +34,18 @@
#include <openssl/evp.h>
krb5_error_code
-k5_sha256(const krb5_data *in, uint8_t out[K5_SHA256_HASHLEN])
+k5_sha256(const krb5_data *in, size_t n, uint8_t out[K5_SHA256_HASHLEN])
{
EVP_MD_CTX *ctx;
+ size_t i;
int ok;
ctx = EVP_MD_CTX_new();
if (ctx == NULL)
return ENOMEM;
ok = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
- ok = ok && EVP_DigestUpdate(ctx, in->data, in->length);
+ for (i = 0; i < n; i++)
+ ok = ok && EVP_DigestUpdate(ctx, in[i].data, in[i].length);
ok = ok && EVP_DigestFinal_ex(ctx, out, NULL);
EVP_MD_CTX_free(ctx);
return ok ? 0 : ENOMEM;
diff --git a/src/lib/krb5/rcache/rc_conv.c b/src/lib/krb5/rcache/rc_conv.c
index 0e021f5d8..f2fe528ac 100644
--- a/src/lib/krb5/rcache/rc_conv.c
+++ b/src/lib/krb5/rcache/rc_conv.c
@@ -58,7 +58,7 @@ krb5_rc_hash_message(krb5_context context, const krb5_data *message,
*out = NULL;
/* Calculate the binary checksum. */
- retval = k5_sha256(message, cksum);
+ retval = k5_sha256(message, 1, cksum);
if (retval)
return retval;

View File

@ -0,0 +1,242 @@
From ab814a990f109357fc4b505169792f9d4d5b5155 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 24 Feb 2020 15:58:59 -0500
Subject: [PATCH] Allow certauth modules to set hw-authent flag
In PKINIT, if a certauth module returns KRB5_CERTAUTH_HWAUTH from its
authorize method, set the hw-authent flag in the ticket.
ticket: 8879 (new)
(cherry picked from commit 50fb43b4a2d97ce2cd53e1ced30e8e8224fede70)
(cherry picked from commit d23b2ed4f06fa77cd021814834dd1391ef6f452f)
---
doc/plugindev/certauth.rst | 7 +++++--
src/include/krb5/certauth_plugin.h | 9 ++++++---
src/lib/krb5/error_tables/k5e1_err.et | 1 +
src/plugins/certauth/test/Makefile.in | 4 ++--
src/plugins/certauth/test/main.c | 11 +++++++++--
src/plugins/preauth/pkinit/pkinit_srv.c | 24 ++++++++++++++++--------
src/tests/t_certauth.py | 13 +++++++++++++
7 files changed, 52 insertions(+), 17 deletions(-)
diff --git a/doc/plugindev/certauth.rst b/doc/plugindev/certauth.rst
index 8a7f7c5eb..3b715f738 100644
--- a/doc/plugindev/certauth.rst
+++ b/doc/plugindev/certauth.rst
@@ -15,8 +15,11 @@ principal. **authorize** receives the DER-encoded certificate, the
requested client principal, and a pointer to the client's
krb5_db_entry (for modules that link against libkdb5). It returns the
authorization status and optionally outputs a list of authentication
-indicator strings to be added to the ticket. A module must use its
-own internal or library-provided ASN.1 certificate decoder.
+indicator strings to be added to the ticket. Beginning in release
+1.19, the authorize method can request that the hardware
+authentication bit be set in the ticket by returning
+**KRB5_CERTAUTH_HWAUTH**. A module must use its own internal or
+library-provided ASN.1 certificate decoder.
A module can optionally create and destroy module data with the
**init** and **fini** methods. Module data objects last for the
diff --git a/src/include/krb5/certauth_plugin.h b/src/include/krb5/certauth_plugin.h
index 3074790f8..3466cf345 100644
--- a/src/include/krb5/certauth_plugin.h
+++ b/src/include/krb5/certauth_plugin.h
@@ -85,14 +85,17 @@ typedef void
(*krb5_certauth_fini_fn)(krb5_context context, krb5_certauth_moddata moddata);
/*
- * Mandatory:
- * Return 0 if the DER-encoded cert is authorized for PKINIT authentication by
- * princ; otherwise return one of the following error codes:
+ * Mandatory: return 0 or KRB5_CERTAUTH_HWAUTH if the DER-encoded cert is
+ * authorized for PKINIT authentication by princ; otherwise return one of the
+ * following error codes:
* - KRB5KDC_ERR_CLIENT_NAME_MISMATCH - incorrect SAN value
* - KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE - incorrect EKU
* - KRB5KDC_ERR_CERTIFICATE_MISMATCH - other extension error
* - KRB5_PLUGIN_NO_HANDLE - the module has no opinion about cert
*
+ * Returning KRB5_CERTAUTH_HWAUTH will cause the hw-authent flag to be set in
+ * the issued ticket (new in release 1.19).
+ *
* - opts is used by built-in modules to receive internal data, and must be
* ignored by other modules.
* - db_entry receives the client principal database entry, and can be ignored
diff --git a/src/lib/krb5/error_tables/k5e1_err.et b/src/lib/krb5/error_tables/k5e1_err.et
index ade5caecf..abd9f3bfe 100644
--- a/src/lib/krb5/error_tables/k5e1_err.et
+++ b/src/lib/krb5/error_tables/k5e1_err.et
@@ -42,4 +42,5 @@ error_code KRB5_KCM_MALFORMED_REPLY, "Malformed reply from KCM daemon"
error_code KRB5_KCM_RPC_ERROR, "Mach RPC error communicating with KCM daemon"
error_code KRB5_KCM_REPLY_TOO_BIG, "KCM daemon reply too big"
error_code KRB5_KCM_NO_SERVER, "No KCM server found"
+error_code KRB5_CERTAUTH_HWAUTH, "Authorize and set hw-authent ticket flag"
end
diff --git a/src/plugins/certauth/test/Makefile.in b/src/plugins/certauth/test/Makefile.in
index d3524084c..e94c13845 100644
--- a/src/plugins/certauth/test/Makefile.in
+++ b/src/plugins/certauth/test/Makefile.in
@@ -5,8 +5,8 @@ LIBBASE=certauth_test
LIBMAJOR=0
LIBMINOR=0
RELDIR=../plugins/certauth/test
-SHLIB_EXPDEPS=$(KRB5_BASE_DEPLIBS)
-SHLIB_EXPLIBS=$(KRB5_BASE_LIBS)
+SHLIB_EXPDEPS=$(KDB5_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+SHLIB_EXPLIBS=$(KDB5_LIBS) $(KRB5_BASE_LIBS)
STLIBOBJS=main.o
diff --git a/src/plugins/certauth/test/main.c b/src/plugins/certauth/test/main.c
index 77641230c..d4633b8cd 100644
--- a/src/plugins/certauth/test/main.c
+++ b/src/plugins/certauth/test/main.c
@@ -31,6 +31,7 @@
*/
#include <k5-int.h>
+#include <kdb.h>
#include "krb5/certauth_plugin.h"
struct krb5_certauth_moddata_st {
@@ -131,7 +132,8 @@ has_cn(krb5_context context, const uint8_t *cert, size_t cert_len,
/*
* Test module 2 returns OK if princ matches the CN part of the subject name,
- * and returns indicators of the module name and princ.
+ * and returns indicators of the module name and princ. If the "hwauth" string
+ * attribute is set on db_entry, it returns KRB5_CERTAUTH_HWAUTH.
*/
static krb5_error_code
test2_authorize(krb5_context context, krb5_certauth_moddata moddata,
@@ -141,7 +143,7 @@ test2_authorize(krb5_context context, krb5_certauth_moddata moddata,
char ***authinds_out)
{
krb5_error_code ret;
- char *name = NULL, **ais = NULL;
+ char *name = NULL, *strval = NULL, **ais = NULL;
*authinds_out = NULL;
@@ -167,6 +169,11 @@ test2_authorize(krb5_context context, krb5_certauth_moddata moddata,
ais = NULL;
+ ret = krb5_dbe_get_string(context, (krb5_db_entry *)db_entry, "hwauth",
+ &strval);
+ ret = (strval != NULL) ? KRB5_CERTAUTH_HWAUTH : 0;
+ krb5_dbe_free_string(context, strval);
+
cleanup:
krb5_free_unparsed_name(context, name);
return ret;
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
index feca11806..3ae56c064 100644
--- a/src/plugins/preauth/pkinit/pkinit_srv.c
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -320,12 +320,12 @@ static krb5_error_code
authorize_cert(krb5_context context, certauth_handle *certauth_modules,
pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx,
krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
- krb5_principal client)
+ krb5_principal client, krb5_boolean *hwauth_out)
{
krb5_error_code ret;
certauth_handle h;
struct certauth_req_opts opts;
- krb5_boolean accepted = FALSE;
+ krb5_boolean accepted = FALSE, hwauth = FALSE;
uint8_t *cert;
size_t i, cert_len;
void *db_ent = NULL;
@@ -347,9 +347,10 @@ authorize_cert(krb5_context context, certauth_handle *certauth_modules,
/*
* Check the certificate against each certauth module. For the certificate
- * to be authorized at least one module must return 0, and no module can an
- * error code other than KRB5_PLUGIN_NO_HANDLE (pass). Add indicators from
- * modules that return 0 or pass.
+ * to be authorized at least one module must return 0 or
+ * KRB5_CERTAUTH_HWAUTH, and no module can return an error code other than
+ * KRB5_PLUGIN_NO_HANDLE (pass). Add indicators from modules that return 0
+ * or pass.
*/
ret = KRB5_PLUGIN_NO_HANDLE;
for (i = 0; certauth_modules != NULL && certauth_modules[i] != NULL; i++) {
@@ -359,6 +360,8 @@ authorize_cert(krb5_context context, certauth_handle *certauth_modules,
&opts, db_ent, &ais);
if (ret == 0)
accepted = TRUE;
+ else if (ret == KRB5_CERTAUTH_HWAUTH)
+ accepted = hwauth = TRUE;
else if (ret != KRB5_PLUGIN_NO_HANDLE)
goto cleanup;
@@ -374,6 +377,7 @@ authorize_cert(krb5_context context, certauth_handle *certauth_modules,
}
}
+ *hwauth_out = hwauth;
ret = accepted ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
cleanup:
@@ -430,7 +434,7 @@ pkinit_server_verify_padata(krb5_context context,
int is_signed = 1;
krb5_pa_data **e_data = NULL;
krb5_kdcpreauth_modreq modreq = NULL;
- krb5_boolean valid_freshness_token = FALSE;
+ krb5_boolean valid_freshness_token = FALSE, hwauth = FALSE;
char **sp;
pkiDebug("pkinit_verify_padata: entered!\n");
@@ -494,7 +498,7 @@ pkinit_server_verify_padata(krb5_context context,
}
if (is_signed) {
retval = authorize_cert(context, moddata->certauth_modules, plgctx,
- reqctx, cb, rock, request->client);
+ reqctx, cb, rock, request->client, &hwauth);
if (retval)
goto cleanup;
@@ -613,6 +617,8 @@ pkinit_server_verify_padata(krb5_context context,
/* remember to set the PREAUTH flag in the reply */
enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
+ if (hwauth)
+ enc_tkt_reply->flags |= TKT_FLG_HW_AUTH;
modreq = (krb5_kdcpreauth_modreq)reqctx;
reqctx = NULL;
@@ -1044,7 +1050,9 @@ pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype)
{
if (patype == KRB5_PADATA_PKINIT_KX)
return PA_INFO;
- return PA_SUFFICIENT | PA_REPLACES_KEY | PA_TYPED_E_DATA;
+ /* PKINIT does not normally set the hw-authent ticket flag, but a
+ * certauth module can cause it to do so. */
+ return PA_SUFFICIENT | PA_REPLACES_KEY | PA_TYPED_E_DATA | PA_HARDWARE;
}
static krb5_preauthtype supported_server_pa_types[] = {
diff --git a/src/tests/t_certauth.py b/src/tests/t_certauth.py
index 9c7094525..0fe0fdb4a 100644
--- a/src/tests/t_certauth.py
+++ b/src/tests/t_certauth.py
@@ -43,4 +43,17 @@ out = realm.kinit("user2@KRBTEST.COM",
expected_code=1,
expected_msg='kinit: Certificate mismatch')
+# Test the KRB5_CERTAUTH_HWAUTH return code.
+mark('hw-authent flag tests')
+# First test +requires_hwauth without causing the hw-authent ticket
+# flag to be set. This currently results in a preauth loop.
+realm.run([kadminl, 'modprinc', '+requires_hwauth', realm.user_princ])
+realm.kinit(realm.user_princ,
+ flags=['-X', 'X509_user_identity=%s' % file_identity],
+ expected_code=1, expected_msg='Looping detected')
+# Cause the test2 module to return KRB5_CERTAUTH_HWAUTH and try again.
+realm.run([kadminl, 'setstr', realm.user_princ, 'hwauth', 'x'])
+realm.kinit(realm.user_princ,
+ flags=['-X', 'X509_user_identity=%s' % file_identity])
+
success("certauth tests")

View File

@ -1,229 +0,0 @@
From 2b9e79d58b28196dba5f7d3ff2f32ca577444ddc Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sat, 31 Mar 2018 10:43:49 -0400
Subject: [PATCH] Be more careful asking for AS key in SPAKE client
Asking for the AS key too early can result in password prompts in
situations where SPAKE won't proceed, such as when the KDC offers only
second factor types not supported by the client.
In spake_prep_questions(), decode the received message and make sure
it's a challenge with a supported group and second factor type
(SF-NONE at the moment). Save the decoded message and use it in
spake_process(). Do not retrieve the AS key at the beginning of
spake_process(); instead do so in process_challenge() after checking
the challenge group and factor types.
Move contains_sf_none() earlier in the file so that it can be used by
spake_prep_questions() without a prototype.
ticket: 8659
(cherry picked from commit f240f1b0d324312be8aa59ead7cfbe0c329ed064)
---
src/plugins/preauth/spake/spake_client.c | 111 ++++++++++++++---------
1 file changed, 66 insertions(+), 45 deletions(-)
diff --git a/src/plugins/preauth/spake/spake_client.c b/src/plugins/preauth/spake/spake_client.c
index d72bd64aa..47a6ba26c 100644
--- a/src/plugins/preauth/spake/spake_client.c
+++ b/src/plugins/preauth/spake/spake_client.c
@@ -39,12 +39,26 @@
#include <krb5/clpreauth_plugin.h>
typedef struct reqstate_st {
+ krb5_pa_spake *msg; /* set in prep_questions, used in process */
krb5_keyblock *initial_key;
krb5_data *support;
krb5_data thash;
krb5_data spakeresult;
} reqstate;
+/* Return true if SF-NONE is present in factors. */
+static krb5_boolean
+contains_sf_none(krb5_spake_factor **factors)
+{
+ int i;
+
+ for (i = 0; factors != NULL && factors[i] != NULL; i++) {
+ if (factors[i]->type == SPAKE_SF_NONE)
+ return TRUE;
+ }
+ return FALSE;
+}
+
static krb5_error_code
spake_init(krb5_context context, krb5_clpreauth_moddata *moddata_out)
{
@@ -77,6 +91,7 @@ spake_request_fini(krb5_context context, krb5_clpreauth_moddata moddata,
{
reqstate *st = (reqstate *)modreq;
+ k5_free_pa_spake(context, st->msg);
krb5_free_keyblock(context, st->initial_key);
krb5_free_data(context, st->support);
krb5_free_data_contents(context, &st->thash);
@@ -92,16 +107,42 @@ spake_prep_questions(krb5_context context, krb5_clpreauth_moddata moddata,
krb5_data *enc_req, krb5_data *enc_prev_req,
krb5_pa_data *pa_data)
{
+ krb5_error_code ret;
+ groupstate *gstate = (groupstate *)moddata;
reqstate *st = (reqstate *)modreq;
+ krb5_data in_data;
+ krb5_spake_challenge *ch;
if (st == NULL)
return ENOMEM;
- if (st->initial_key == NULL && pa_data->length > 0)
+
+ /* We don't need to ask any questions to send a support message. */
+ if (pa_data->length == 0)
+ return 0;
+
+ /* Decode the incoming message, replacing any previous one in the request
+ * state. If we can't decode it, we have no questions to ask. */
+ k5_free_pa_spake(context, st->msg);
+ st->msg = NULL;
+ in_data = make_data(pa_data->contents, pa_data->length);
+ ret = decode_krb5_pa_spake(&in_data, &st->msg);
+ if (ret)
+ return (ret == ENOMEM) ? ENOMEM : 0;
+
+ if (st->msg->choice == SPAKE_MSGTYPE_CHALLENGE) {
+ ch = &st->msg->u.challenge;
+ if (!group_is_permitted(gstate, ch->group))
+ return 0;
+ /* When second factor support is implemented, we should ask questions
+ * based on the factors in the challenge. */
+ if (!contains_sf_none(ch->factors))
+ return 0;
+ /* We will need the AS key to respond to the challenge. */
cb->need_as_key(context, rock);
-
- /* When second-factor is implemented, we should ask questions based on the
- * factors in the challenge. */
-
+ } else if (st->msg->choice == SPAKE_MSGTYPE_ENCDATA) {
+ /* When second factor support is implemented, we should decrypt the
+ * encdata message and ask questions based on the factor data. */
+ }
return 0;
}
@@ -136,19 +177,6 @@ send_support(krb5_context context, groupstate *gstate, reqstate *st,
return convert_to_padata(support, pa_out);
}
-/* Return true if SF-NONE is present in factors. */
-static krb5_boolean
-contains_sf_none(krb5_spake_factor **factors)
-{
- int i;
-
- for (i = 0; factors != NULL && factors[i] != NULL; i++) {
- if (factors[i]->type == SPAKE_SF_NONE)
- return TRUE;
- }
- return FALSE;
-}
-
static krb5_error_code
process_challenge(krb5_context context, groupstate *gstate, reqstate *st,
krb5_spake_challenge *ch, const krb5_data *der_msg,
@@ -157,7 +185,7 @@ process_challenge(krb5_context context, groupstate *gstate, reqstate *st,
const krb5_data *der_req, krb5_pa_data ***pa_out)
{
krb5_error_code ret;
- krb5_keyblock *k0 = NULL, *k1 = NULL;
+ krb5_keyblock *k0 = NULL, *k1 = NULL, *as_key;
krb5_spake_factor factor;
krb5_pa_spake msg;
krb5_data *der_factor = NULL, *response;
@@ -167,8 +195,8 @@ process_challenge(krb5_context context, groupstate *gstate, reqstate *st,
enc_factor.ciphertext = empty_data();
- /* Not expected if we already computed the SPAKE result. */
- if (st->spakeresult.length != 0)
+ /* Not expected if we processed a challenge and didn't reject it. */
+ if (st->initial_key != NULL)
return KRB5KDC_ERR_PREAUTH_FAILED;
if (!group_is_permitted(gstate, ch->group)) {
@@ -193,6 +221,12 @@ process_challenge(krb5_context context, groupstate *gstate, reqstate *st,
if (!contains_sf_none(ch->factors))
return KRB5KDC_ERR_PREAUTH_FAILED;
+ ret = cb->get_as_key(context, rock, &as_key);
+ if (ret)
+ goto cleanup;
+ ret = krb5_copy_keyblock(context, as_key, &st->initial_key);
+ if (ret)
+ goto cleanup;
ret = derive_wbytes(context, ch->group, st->initial_key, &wbytes);
if (ret)
goto cleanup;
@@ -267,7 +301,7 @@ process_encdata(krb5_context context, reqstate *st, krb5_enc_data *enc,
krb5_pa_data ***pa_out)
{
/* Not expected if we haven't sent a response yet. */
- if (st->spakeresult.length == 0)
+ if (st->initial_key == NULL || st->spakeresult.length == 0)
return KRB5KDC_ERR_PREAUTH_FAILED;
/*
@@ -292,9 +326,7 @@ spake_process(krb5_context context, krb5_clpreauth_moddata moddata,
krb5_error_code ret;
groupstate *gstate = (groupstate *)moddata;
reqstate *st = (reqstate *)modreq;
- krb5_pa_spake *msg;
krb5_data in_data;
- krb5_keyblock *as_key;
if (st == NULL)
return ENOMEM;
@@ -306,34 +338,23 @@ spake_process(krb5_context context, krb5_clpreauth_moddata moddata,
return send_support(context, gstate, st, pa_out);
}
- /* We need the initial reply key to process any non-trivial message. */
- if (st->initial_key == NULL) {
- ret = cb->get_as_key(context, rock, &as_key);
- if (ret)
- return ret;
- ret = krb5_copy_keyblock(context, as_key, &st->initial_key);
- if (ret)
- return ret;
- }
-
- in_data = make_data(pa_in->contents, pa_in->length);
- ret = decode_krb5_pa_spake(&in_data, &msg);
- if (ret)
- return ret;
-
- if (msg->choice == SPAKE_MSGTYPE_CHALLENGE) {
- ret = process_challenge(context, gstate, st, &msg->u.challenge,
+ if (st->msg == NULL) {
+ /* The message failed to decode in spake_prep_questions(). */
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ } else if (st->msg->choice == SPAKE_MSGTYPE_CHALLENGE) {
+ in_data = make_data(pa_in->contents, pa_in->length);
+ ret = process_challenge(context, gstate, st, &st->msg->u.challenge,
&in_data, cb, rock, prompter, prompter_data,
der_req, pa_out);
- } else if (msg->choice == SPAKE_MSGTYPE_ENCDATA) {
- ret = process_encdata(context, st, &msg->u.encdata, cb, rock, prompter,
- prompter_data, der_prev_req, der_req, pa_out);
+ } else if (st->msg->choice == SPAKE_MSGTYPE_ENCDATA) {
+ ret = process_encdata(context, st, &st->msg->u.encdata, cb, rock,
+ prompter, prompter_data, der_prev_req, der_req,
+ pa_out);
} else {
/* Unexpected message type */
ret = KRB5KDC_ERR_PREAUTH_FAILED;
}
- k5_free_pa_spake(context, msg);
return ret;
}

View File

@ -1,156 +0,0 @@
From ce06474e3b12430480374f923c25bae9581fb146 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Fri, 9 Nov 2018 15:12:21 -0500
Subject: [PATCH] Become FIPS-aware
A lot of the FIPS error conditions from OpenSSL are incredibly
mysterious (at best, things return NULL unexpectedly; at worst,
internal assertions are tripped; most of the time, you just get
ENOMEM). In order to cope with this, we need to have some level of
awareness of what we can and can't safely call.
This will slow down some calls slightly (FIPS_mode() takes multiple
locks), but not for any crypto we care about - AES is fine, for
instance.
---
src/lib/crypto/openssl/enc_provider/camellia.c | 6 ++++++
src/lib/crypto/openssl/enc_provider/des.c | 9 +++++++++
src/lib/crypto/openssl/enc_provider/rc4.c | 13 ++++++++++++-
src/lib/crypto/openssl/hash_provider/hash_evp.c | 4 ++++
src/lib/crypto/openssl/hmac.c | 6 +++++-
5 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/src/lib/crypto/openssl/enc_provider/camellia.c b/src/lib/crypto/openssl/enc_provider/camellia.c
index 2da691329..f79679a0b 100644
--- a/src/lib/crypto/openssl/enc_provider/camellia.c
+++ b/src/lib/crypto/openssl/enc_provider/camellia.c
@@ -304,6 +304,9 @@ krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
unsigned char blockY[CAMELLIA_BLOCK_SIZE], blockB[CAMELLIA_BLOCK_SIZE];
struct iov_cursor cursor;
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
if (output->length < CAMELLIA_BLOCK_SIZE)
return KRB5_BAD_MSIZE;
@@ -331,6 +334,9 @@ static krb5_error_code
krb5int_camellia_init_state (const krb5_keyblock *key, krb5_keyusage usage,
krb5_data *state)
{
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
state->length = 16;
state->data = (void *) malloc(16);
if (state->data == NULL)
diff --git a/src/lib/crypto/openssl/enc_provider/des.c b/src/lib/crypto/openssl/enc_provider/des.c
index a662db512..7d17d287e 100644
--- a/src/lib/crypto/openssl/enc_provider/des.c
+++ b/src/lib/crypto/openssl/enc_provider/des.c
@@ -85,6 +85,9 @@ k5_des_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
EVP_CIPHER_CTX *ctx;
krb5_boolean empty;
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
ret = validate(key, ivec, data, num_data, &empty);
if (ret != 0 || empty)
return ret;
@@ -133,6 +136,9 @@ k5_des_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
EVP_CIPHER_CTX *ctx;
krb5_boolean empty;
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
ret = validate(key, ivec, data, num_data, &empty);
if (ret != 0 || empty)
return ret;
@@ -182,6 +188,9 @@ k5_des_cbc_mac(krb5_key key, const krb5_crypto_iov *data, size_t num_data,
DES_key_schedule sched;
krb5_boolean empty;
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
ret = validate(key, ivec, data, num_data, &empty);
if (ret != 0)
return ret;
diff --git a/src/lib/crypto/openssl/enc_provider/rc4.c b/src/lib/crypto/openssl/enc_provider/rc4.c
index 7f3c086ed..a3f2a7442 100644
--- a/src/lib/crypto/openssl/enc_provider/rc4.c
+++ b/src/lib/crypto/openssl/enc_provider/rc4.c
@@ -66,6 +66,9 @@ k5_arcfour_docrypt(krb5_key key,const krb5_data *state, krb5_crypto_iov *data,
EVP_CIPHER_CTX *ctx = NULL;
struct arcfour_state *arcstate;
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
arcstate = (state != NULL) ? (struct arcfour_state *) state->data : NULL;
if (arcstate != NULL) {
ctx = arcstate->ctx;
@@ -113,7 +116,12 @@ k5_arcfour_docrypt(krb5_key key,const krb5_data *state, krb5_crypto_iov *data,
static void
k5_arcfour_free_state(krb5_data *state)
{
- struct arcfour_state *arcstate = (struct arcfour_state *) state->data;
+ struct arcfour_state *arcstate;
+
+ if (FIPS_mode())
+ return;
+
+ arcstate = (struct arcfour_state *) state->data;
EVP_CIPHER_CTX_free(arcstate->ctx);
free(arcstate);
@@ -125,6 +133,9 @@ k5_arcfour_init_state(const krb5_keyblock *key,
{
struct arcfour_state *arcstate;
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
/* Create a state structure with an uninitialized context. */
arcstate = calloc(1, sizeof(*arcstate));
if (arcstate == NULL)
diff --git a/src/lib/crypto/openssl/hash_provider/hash_evp.c b/src/lib/crypto/openssl/hash_provider/hash_evp.c
index 957ed8d9c..8c1fd7f59 100644
--- a/src/lib/crypto/openssl/hash_provider/hash_evp.c
+++ b/src/lib/crypto/openssl/hash_provider/hash_evp.c
@@ -64,12 +64,16 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
static krb5_error_code
hash_md4(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
{
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
return hash_evp(EVP_md4(), data, num_data, output);
}
static krb5_error_code
hash_md5(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
{
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
return hash_evp(EVP_md5(), data, num_data, output);
}
diff --git a/src/lib/crypto/openssl/hmac.c b/src/lib/crypto/openssl/hmac.c
index b2db6ec02..d94d9ac94 100644
--- a/src/lib/crypto/openssl/hmac.c
+++ b/src/lib/crypto/openssl/hmac.c
@@ -103,7 +103,11 @@ map_digest(const struct krb5_hash_provider *hash)
return EVP_sha256();
else if (!strncmp(hash->hash_name, "SHA-384",7))
return EVP_sha384();
- else if (!strncmp(hash->hash_name, "MD5", 3))
+
+ if (FIPS_mode())
+ return NULL;
+
+ if (!strncmp(hash->hash_name, "MD5", 3))
return EVP_md5();
else if (!strncmp(hash->hash_name, "MD4", 3))
return EVP_md4();

View File

@ -1,536 +0,0 @@
From 2bc365f12282cdd83a191478b97f4ea0d9aa60dd Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 19 Feb 2018 21:10:09 -0500
Subject: [PATCH] Convert Python tests to Python 3
Look for python3 in configure.in and verify that we got it. Convert
test code to conform to Python 3.
ticket: 8710 (new)
(cherry picked from commit e23d24beacb73581bbf4351250f3955e6fd44361)
[rharwood@redhat.com: Context skew due to not having LMDB in tests]
---
src/Makefile.in | 1 +
src/configure.in | 6 ++--
src/kadmin/dbutil/t_tdumputil.py | 4 +--
src/tests/jsonwalker.py | 16 +++++------
src/tests/t_cve-2012-1014.py | 2 +-
src/tests/t_cve-2012-1015.py | 2 +-
src/tests/t_hostrealm.py | 4 ++-
src/tests/t_kdb.py | 11 ++++---
src/tests/t_keytab.py | 34 +++++++++++-----------
src/tests/t_mkey.py | 6 ++--
src/tests/t_otp.py | 7 +++--
src/tests/t_tabdump.py | 4 +--
src/util/Makefile.in | 1 +
src/util/k5test.py | 49 +++++++++++++++++---------------
src/util/princflags.py | 25 ++++++++--------
15 files changed, 88 insertions(+), 84 deletions(-)
diff --git a/src/Makefile.in b/src/Makefile.in
index 77beff8bc..79b8d5f98 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -533,6 +533,7 @@ runenv.py: pyrunenv.vals
clean-unix::
$(RM) runenv.py runenv.pyc pyrunenv.vals
+ $(RM) -r __pycache__
COV_BUILD= cov-build
COV_ANALYZE= cov-analyze
diff --git a/src/configure.in b/src/configure.in
index 3f45784b5..00cb297b8 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -1098,15 +1098,13 @@ fi
AC_SUBST(HAVE_RUNTEST)
# For Python tests.
-AC_CHECK_PROG(PYTHON,python2,python2)
+AC_CHECK_PROG(PYTHON,python3,python3)
if text x"$PYTHON" = x; then
AC_CHECK_PROG(PYTHON,python,python)
fi
HAVE_PYTHON=no
if test x"$PYTHON" != x; then
- # k5test.py requires python 2.4 (for the subprocess module).
- # Some code needs python 2.5 (for syntax like conditional expressions).
- wantver="(sys.hexversion >= 0x2050000 and sys.hexversion < 0x3000000)"
+ wantver="(sys.hexversion >= 0x3000000)"
if "$PYTHON" -c "import sys; sys.exit(not $wantver and 1 or 0)"; then
HAVE_PYTHON=yes
fi
diff --git a/src/kadmin/dbutil/t_tdumputil.py b/src/kadmin/dbutil/t_tdumputil.py
index 52e356533..47b2aa7a3 100755
--- a/src/kadmin/dbutil/t_tdumputil.py
+++ b/src/kadmin/dbutil/t_tdumputil.py
@@ -6,8 +6,8 @@ realm = K5Realm(create_kdb=False)
def compare(s, expected, msg):
if s == expected:
return
- print 'expected:', repr(expected)
- print 'got:', repr(s)
+ print('expected:', repr(expected))
+ print('got:', repr(s))
fail(msg)
out = realm.run(['./t_tdumputil', '2', 'field1', 'field2',
diff --git a/src/tests/jsonwalker.py b/src/tests/jsonwalker.py
index 942ca2db7..7a0675e08 100644
--- a/src/tests/jsonwalker.py
+++ b/src/tests/jsonwalker.py
@@ -2,8 +2,8 @@ import sys
try:
import cjson
except ImportError:
- print "Warning: skipping audit log verification because the cjson module" \
- " is unavailable"
+ print("Warning: skipping audit log verification because the cjson module" \
+ " is unavailable")
sys.exit(0)
from collections import defaultdict
from optparse import OptionParser
@@ -22,10 +22,10 @@ class Parser(object):
result = self.parse(logs)
if len(result) != len(self.defaults):
diff = set(self.defaults.keys()).difference(result.keys())
- print 'Test failed.'
- print 'The following attributes were not set:'
+ print('Test failed.')
+ print('The following attributes were not set:')
for it in diff:
- print it
+ print(it)
sys.exit(1)
def flatten(self, defaults):
@@ -42,7 +42,7 @@ class Parser(object):
result = dict()
for path,value in self._walk(defaults):
if path in result:
- print 'Warning: attribute path %s already exists' % path
+ print('Warning: attribute path %s already exists' % path)
result[path] = value
return result
@@ -60,7 +60,7 @@ class Parser(object):
if v is not None:
dv = self.DEFAULTS[type(v)]
else:
- print 'Warning: attribute %s is set to None' % a
+ print('Warning: attribute %s is set to None' % a)
continue
# by now we have default value
if v != dv:
@@ -96,7 +96,7 @@ if __name__ == '__main__':
content.append(cjson.decode(l.rstrip()))
f.close()
else:
- print 'Input file in jason format is required'
+ print('Input file in jason format is required')
exit()
defaults = None
diff --git a/src/tests/t_cve-2012-1014.py b/src/tests/t_cve-2012-1014.py
index dcff95f6e..8447e0ee7 100755
--- a/src/tests/t_cve-2012-1014.py
+++ b/src/tests/t_cve-2012-1014.py
@@ -20,7 +20,7 @@ x2 = base64.b16decode('A44F304DA007030500FEDCBA90A10E30' +
'01')
for x in range(11, 128):
- s.sendto(''.join([x1, chr(x), x2]), a)
+ s.sendto(x1 + bytes([x]) + x2, a)
# Make sure kinit still works.
diff --git a/src/tests/t_cve-2012-1015.py b/src/tests/t_cve-2012-1015.py
index 28b1e619b..ae5678cac 100755
--- a/src/tests/t_cve-2012-1015.py
+++ b/src/tests/t_cve-2012-1015.py
@@ -27,7 +27,7 @@ x1 = base64.b16decode('6A81A030819DA103020105A20302010A' +
x2 = base64.b16decode('A8083006020106020112')
for x in range(0, 128):
- s.sendto(''.join([x1, chr(x), x2]), a)
+ s.sendto(x1 + bytes([x]) + x2, a)
# Make sure kinit still works.
diff --git a/src/tests/t_hostrealm.py b/src/tests/t_hostrealm.py
index 256ba2a38..beea6f3bc 100755
--- a/src/tests/t_hostrealm.py
+++ b/src/tests/t_hostrealm.py
@@ -119,7 +119,9 @@ testd(realm, 'KRBTEST.COM', 'default_realm profile', env=notest2)
# see the first. Remove the profile default_realm setting to expose
# this behavior.
remove_default = {'libdefaults': {'default_realm': None}}
-nodefault_conf = dict(disable_conf.items() + remove_default.items())
+# Python 3.5+: nodefault_conf = {**disable_conf, **remove_default}
+nodefault_conf = dict(list(disable_conf.items()) +
+ list(remove_default.items()))
nodefault = realm.special_env('nodefault', False, krb5_conf=nodefault_conf)
testd(realm, 'one', 'default_realm test1', env=nodefault)
diff --git a/src/tests/t_kdb.py b/src/tests/t_kdb.py
index 983cd93c8..42237f7a1 100755
--- a/src/tests/t_kdb.py
+++ b/src/tests/t_kdb.py
@@ -1,6 +1,5 @@
from k5test import *
import time
-from itertools import imap
# Run kdbtest against the BDB module.
realm = K5Realm(create_kdb=False)
@@ -51,7 +50,7 @@ else:
def slap_add(ldif):
proc = subprocess.Popen([slapadd, '-b', 'cn=config', '-F', slapd_conf],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
+ stderr=subprocess.STDOUT, universal_newlines=True)
(out, dummy) = proc.communicate(ldif)
output(out)
return proc.wait()
@@ -98,7 +97,7 @@ if slap_add('include: file://%s\n' % schema) != 0:
ldap_homes = ['/etc/ldap', '/etc/openldap', '/usr/local/etc/openldap',
'/usr/local/etc/ldap']
local_schema_path = '/schema/core.ldif'
-core_schema = next((i for i in imap(lambda x:x+local_schema_path, ldap_homes)
+core_schema = next((i for i in map(lambda x:x+local_schema_path, ldap_homes)
if os.path.isfile(i)), None)
if core_schema:
if slap_add('include: file://%s\n' % core_schema) != 0:
@@ -114,7 +113,7 @@ atexit.register(kill_slapd)
out = open(slapd_out, 'w')
subprocess.call([slapd, '-h', ldap_uri, '-F', slapd_conf], stdout=out,
- stderr=out)
+ stderr=out, universal_newlines=True)
out.close()
pidf = open(slapd_pidfile, 'r')
slapd_pid = int(pidf.read())
@@ -158,7 +157,7 @@ def ldap_search(args):
proc = subprocess.Popen([ldapsearch, '-H', ldap_uri, '-b', top_dn,
'-D', admin_dn, '-w', admin_pw, args],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
+ stderr=subprocess.STDOUT, universal_newlines=True)
(out, dummy) = proc.communicate()
return out
@@ -166,7 +165,7 @@ def ldap_modify(ldif, args=[]):
proc = subprocess.Popen([ldapmodify, '-H', ldap_uri, '-D', admin_dn,
'-x', '-w', admin_pw] + args,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
+ stderr=subprocess.STDOUT, universal_newlines=True)
(out, dummy) = proc.communicate(ldif)
output(out)
diff --git a/src/tests/t_keytab.py b/src/tests/t_keytab.py
index 228c36334..8a17ae2eb 100755
--- a/src/tests/t_keytab.py
+++ b/src/tests/t_keytab.py
@@ -90,36 +90,36 @@ test_key_rotate(realm, princ, 2)
# Test that klist -k can read a keytab entry without a 32-bit kvno and
# reports the 8-bit key version.
-record = '\x00\x01' # principal component count
-record += '\x00\x0bKRBTEST.COM' # realm
-record += '\x00\x04user' # principal component
-record += '\x00\x00\x00\x01' # name type (NT-PRINCIPAL)
-record += '\x54\xf7\x4d\x35' # timestamp
-record += '\x02' # key version
-record += '\x00\x12' # enctype
-record += '\x00\x20' # key length
-record += '\x00' * 32 # key bytes
-f = open(realm.keytab, 'w')
-f.write('\x05\x02\x00\x00\x00' + chr(len(record)))
+record = b'\x00\x01' # principal component count
+record += b'\x00\x0bKRBTEST.COM' # realm
+record += b'\x00\x04user' # principal component
+record += b'\x00\x00\x00\x01' # name type (NT-PRINCIPAL)
+record += b'\x54\xf7\x4d\x35' # timestamp
+record += b'\x02' # key version
+record += b'\x00\x12' # enctype
+record += b'\x00\x20' # key length
+record += b'\x00' * 32 # key bytes
+f = open(realm.keytab, 'wb')
+f.write(b'\x05\x02\x00\x00\x00' + bytes([len(record)]))
f.write(record)
f.close()
msg = ' 2 %s' % realm.user_princ
out = realm.run([klist, '-k'], expected_msg=msg)
# Make sure zero-fill isn't treated as a 32-bit kvno.
-f = open(realm.keytab, 'w')
-f.write('\x05\x02\x00\x00\x00' + chr(len(record) + 4))
+f = open(realm.keytab, 'wb')
+f.write(b'\x05\x02\x00\x00\x00' + bytes([len(record) + 4]))
f.write(record)
-f.write('\x00\x00\x00\x00')
+f.write(b'\x00\x00\x00\x00')
f.close()
msg = ' 2 %s' % realm.user_princ
out = realm.run([klist, '-k'], expected_msg=msg)
# Make sure a hand-crafted 32-bit kvno is recognized.
-f = open(realm.keytab, 'w')
-f.write('\x05\x02\x00\x00\x00' + chr(len(record) + 4))
+f = open(realm.keytab, 'wb')
+f.write(b'\x05\x02\x00\x00\x00' + bytes([len(record) + 4]))
f.write(record)
-f.write('\x00\x00\x00\x03')
+f.write(b'\x00\x00\x00\x03')
f.close()
msg = ' 3 %s' % realm.user_princ
out = realm.run([klist, '-k'], expected_msg=msg)
diff --git a/src/tests/t_mkey.py b/src/tests/t_mkey.py
index 48a533059..cbc830235 100755
--- a/src/tests/t_mkey.py
+++ b/src/tests/t_mkey.py
@@ -296,10 +296,10 @@ realm.stop()
# 2. list_mkeys displays the same list as for a post-1.7 KDB.
dumpfile = os.path.join(srctop, 'tests', 'dumpfiles', 'dump.16')
os.remove(stash_file)
-f = open(stash_file, 'w')
+f = open(stash_file, 'wb')
f.write(struct.pack('=HL24s', 16, 24,
- '\xF8\x3E\xFB\xBA\x6D\x80\xD9\x54\xE5\x5D\xF2\xE0'
- '\x94\xAD\x6D\x86\xB5\x16\x37\xEC\x7C\x8A\xBC\x86'))
+ b'\xF8\x3E\xFB\xBA\x6D\x80\xD9\x54\xE5\x5D\xF2\xE0'
+ b'\x94\xAD\x6D\x86\xB5\x16\x37\xEC\x7C\x8A\xBC\x86'))
f.close()
realm.run([kdb5_util, 'load', dumpfile])
nprincs = len(realm.run([kadminl, 'listprincs']).splitlines())
diff --git a/src/tests/t_otp.py b/src/tests/t_otp.py
index 0fd35d576..617a8ecf5 100755
--- a/src/tests/t_otp.py
+++ b/src/tests/t_otp.py
@@ -29,8 +29,8 @@
#
from k5test import *
-from Queue import Empty
-import StringIO
+from queue import Empty
+from io import StringIO
import struct
try:
@@ -120,7 +120,8 @@ class UnixRadiusDaemon(RadiusDaemon):
sock.listen(1)
return (sock, addr)
- def recvRequest(self, (sock, addr)):
+ def recvRequest(self, sock_and_addr):
+ sock, addr = sock_and_addr
conn = sock.accept()[0]
sock.close()
os.remove(addr)
diff --git a/src/tests/t_tabdump.py b/src/tests/t_tabdump.py
index 2a86136dd..49531bf49 100755
--- a/src/tests/t_tabdump.py
+++ b/src/tests/t_tabdump.py
@@ -1,10 +1,10 @@
from k5test import *
import csv
-import StringIO
+from io import StringIO
def tab_csv(s):
- io = StringIO.StringIO(s)
+ io = StringIO(s)
return list(csv.DictReader(io, dialect=csv.excel_tab))
diff --git a/src/util/Makefile.in b/src/util/Makefile.in
index 2611581c1..19a6bd312 100644
--- a/src/util/Makefile.in
+++ b/src/util/Makefile.in
@@ -26,3 +26,4 @@ install:
clean-unix::
$(RM) *.pyc
+ $(RM) -r __pycache__
diff --git a/src/util/k5test.py b/src/util/k5test.py
index bc32877a7..81fac3063 100644
--- a/src/util/k5test.py
+++ b/src/util/k5test.py
@@ -380,16 +380,16 @@ import imp
def fail(msg):
"""Print a message and exit with failure."""
global _current_pass
- print "*** Failure:", msg
+ print("*** Failure:", msg)
if _last_mark:
- print "*** Last mark: %s" % _last_mark
+ print("*** Last mark: %s" % _last_mark)
if _last_cmd:
- print "*** Last command (#%d): %s" % (_cmd_index - 1, _last_cmd)
+ print("*** Last command (#%d): %s" % (_cmd_index - 1, _last_cmd))
if _last_cmd_output:
- print "*** Output of last command:"
+ print("*** Output of last command:")
sys.stdout.write(_last_cmd_output)
if _current_pass:
- print "*** Failed in test pass:", _current_pass
+ print("*** Failed in test pass:", _current_pass)
sys.exit(1)
@@ -465,15 +465,16 @@ def _onexit():
if not verbose:
testlogfile = os.path.join(os.getcwd(), 'testlog')
utildir = os.path.join(srctop, 'util')
- print 'For details, see: %s' % testlogfile
- print 'Or re-run this test script with the -v flag:'
- print ' cd %s' % os.getcwd()
- print ' PYTHONPATH=%s %s %s -v' % \
- (utildir, sys.executable, sys.argv[0])
- print
- print 'Use --debug=NUM to run a command under a debugger. Use'
- print '--stop-after=NUM to stop after a daemon is started in order to'
- print 'attach to it with a debugger. Use --help to see other options.'
+ print('For details, see: %s' % testlogfile)
+ print('Or re-run this test script with the -v flag:')
+ print(' cd %s' % os.getcwd())
+ print(' PYTHONPATH=%s %s %s -v' %
+ (utildir, sys.executable, sys.argv[0]))
+ print()
+ print('Use --debug=NUM to run a command under a debugger. Use')
+ print('--stop-after=NUM to stop after a daemon is started in order to')
+ print('attach to it with a debugger. Use --help to see other')
+ print('options.')
def _onsigint(signum, frame):
@@ -523,8 +524,8 @@ def _get_hostname():
hostname = socket.gethostname()
try:
ai = socket.getaddrinfo(hostname, None, 0, 0, 0, socket.AI_CANONNAME)
- except socket.gaierror, (error, errstr):
- fail('Local hostname "%s" does not resolve: %s.' % (hostname, errstr))
+ except socket.gaierror as e:
+ fail('Local hostname "%s" does not resolve: %s.' % (hostname, e[1]))
(family, socktype, proto, canonname, sockaddr) = ai[0]
try:
name = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD)
@@ -594,7 +595,7 @@ def _match_cmdnum(cmdnum, ind):
def _build_env():
global buildtop, runenv
env = os.environ.copy()
- for (k, v) in runenv.env.iteritems():
+ for (k, v) in runenv.env.items():
if v.find('./') == 0:
env[k] = os.path.join(buildtop, v)
else:
@@ -704,7 +705,8 @@ def _run_cmd(args, env, input=None, expected_code=0, expected_msg=None,
# Run the command and log the result, folding stderr into stdout.
proc = subprocess.Popen(args, stdin=infile, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT, env=env)
+ stderr=subprocess.STDOUT, env=env,
+ universal_newlines=True)
(outdata, dummy_errdata) = proc.communicate(input)
_last_cmd_output = outdata
code = proc.returncode
@@ -734,10 +736,10 @@ def _debug_cmd(args, env, input):
(_cmd_index, _shell_equiv(args)), True)
if input:
print
- print '*** Enter the following input when appropriate:'
- print
- print input
- print
+ print('*** Enter the following input when appropriate:')
+ print()
+ print(input)
+ print()
code = subprocess.call(args, env=env)
output('*** [%d] Completed in debugger with return code %d\n' %
(_cmd_index, code))
@@ -765,7 +767,8 @@ def _start_daemon(args, env, sentinel):
# Start the daemon and look for the sentinel in stdout or stderr.
proc = subprocess.Popen(args, stdin=null_input, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT, env=env)
+ stderr=subprocess.STDOUT, env=env,
+ universal_newlines=True)
_last_cmd_output = ''
while True:
line = proc.stdout.readline()
diff --git a/src/util/princflags.py b/src/util/princflags.py
index f568dd2f1..f645e86e4 100644
--- a/src/util/princflags.py
+++ b/src/util/princflags.py
@@ -1,5 +1,4 @@
import re
-import string
# Module for translating KDB principal flags between string and
# integer forms.
@@ -81,7 +80,7 @@ _prefixlen = len(_prefix)
_flagnames = {}
# Translation table to map hyphens to underscores
-_squash = string.maketrans('-', '_')
+_squash = str.maketrans('-', '_')
# Combined input-to-flag lookup table, to be filled in by
# _setup_tables()
@@ -176,7 +175,7 @@ def flagnum2str(n):
# Return a list of flag names from a flag word.
def flags2namelist(flags):
a = []
- for n in xrange(32):
+ for n in range(32):
if flags & (1 << n):
a.append(flagnum2str(n))
return a
@@ -225,21 +224,21 @@ def speclist2mask(s):
# Print C table of input flag specifiers for lib/kadm5/str_conv.c.
def _print_ftbl():
- print 'static const struct flag_table_row ftbl[] = {'
- a = sorted(pflags.items(), key=lambda (k, v): (v.flag, -v.invert, k))
+ print('static const struct flag_table_row ftbl[] = {')
+ a = sorted(pflags.items(), key=lambda k, v: (v.flag, -v.invert, k))
for k, v in a:
s1 = ' {"%s",' % k
s2 = '%-31s KRB5_KDB_%s,' % (s1, v.flagname())
- print '%-63s %d},' % (s2, 1 if v.invert else 0)
+ print('%-63s %d},' % (s2, 1 if v.invert else 0))
- print '};'
- print '#define NFTBL (sizeof(ftbl) / sizeof(ftbl[0]))'
+ print('};')
+ print('#define NFTBL (sizeof(ftbl) / sizeof(ftbl[0]))')
# Print C table of output flag names for lib/kadm5/str_conv.c.
def _print_outflags():
- print 'static const char *outflags[] = {'
- for i in xrange(32):
+ print('static const char *outflags[] = {')
+ for i in range(32):
flag = 1 << i
if flag > max(_flagnames.keys()):
break
@@ -247,10 +246,10 @@ def _print_outflags():
s = ' "%s",' % _flagnames[flag]
except KeyError:
s = ' NULL,'
- print '%-32s/* 0x%08x */' % (s, flag)
+ print('%-32s/* 0x%08x */' % (s, flag))
- print '};'
- print '#define NOUTFLAGS (sizeof(outflags) / sizeof(outflags[0]))'
+ print('};')
+ print('#define NOUTFLAGS (sizeof(outflags) / sizeof(outflags[0]))')
# Print out C tables to insert into lib/kadm5/str_conv.c.

View File

@ -0,0 +1,53 @@
From cbdae9a9dc2a6af5551d26b32c8d473e1e0ce773 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 30 Mar 2020 15:26:02 -0400
Subject: [PATCH] Correctly import "service@" GSS host-based name
The intended way to specify only a service in a GSS host-based name is
to omit the "@" separator. Some applications include the separator
but no hostname, and this happened to yield wildcard hostname behavior
prior to commit 996353767fe8afa7f67a3b5b465e4d70e18bad7c when
shortname qualification was added. To restore this behavior, check in
parse_hostbased() that at least one character is present after the "@"
separator before copying the hostname. Add a test case to t_gssapi.py.
ticket: 8892
tags: pullup
target_version: 1.18-next
(cherry picked from commit a2f047af0400ba8080dc26033fae2b17534501e2)
(cherry picked from commit dd4364d76925ce1fe21c2ab995554d6af3a2ea12)
---
src/lib/gssapi/krb5/import_name.c | 4 ++--
src/tests/gssapi/t_gssapi.py | 3 +++
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/lib/gssapi/krb5/import_name.c b/src/lib/gssapi/krb5/import_name.c
index da2ab1423..21023dd76 100644
--- a/src/lib/gssapi/krb5/import_name.c
+++ b/src/lib/gssapi/krb5/import_name.c
@@ -102,8 +102,8 @@ parse_hostbased(const char *str, size_t len,
memcpy(service, str, servicelen);
service[servicelen] = '\0';
- /* If present, copy the hostname. */
- if (at != NULL) {
+ /* Copy the hostname if present (at least one character after '@'). */
+ if (len - servicelen > 1) {
hostlen = len - servicelen - 1;
host = malloc(hostlen + 1);
if (host == NULL) {
diff --git a/src/tests/gssapi/t_gssapi.py b/src/tests/gssapi/t_gssapi.py
index 54d5cf549..ecf982604 100755
--- a/src/tests/gssapi/t_gssapi.py
+++ b/src/tests/gssapi/t_gssapi.py
@@ -47,6 +47,9 @@ realm.run(['./t_accname', 'p:service2/calvin', 'h:service2'],
expected_msg='service2/calvin')
realm.run(['./t_accname', 'p:service2/calvin', 'h:service1'], expected_code=1,
expected_msg=' found in keytab but does not match server principal')
+# Regression test for #8892 (trailing @ in name).
+realm.run(['./t_accname', 'p:service1/andrew', 'h:service1@'],
+ expected_msg='service1/abraham')
# Test with acceptor name containing service and host. Use the
# client's un-canonicalized hostname as acceptor input to mirror what

View File

@ -0,0 +1,426 @@
From ff6cf2a0545d12a020572dd137fd22d1edc726e4 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Fri, 28 Feb 2020 10:11:49 +0100
Subject: [PATCH] Do expiration warnings for all init_creds APIs
Move the password expiration warning code from gic_pwd.c to
get_in_tkt.c. Call it from init_creds_step_reply() on successful
completion.
[ghudson@mit.edu: added test case; simplified doc comment; moved call
site to init_creds_step_reply(); rewrote commit message]
ticket: 8893 (new)
(cherry picked from commit e1efb890f7ac31b32c68ab816ef118dbfb5a8c7e)
(cherry picked from commit c136cfe050d203c910624573a33247fde2889b09)
---
src/include/krb5/krb5.hin | 9 ++-
src/lib/krb5/krb/get_in_tkt.c | 112 ++++++++++++++++++++++++++++++
src/lib/krb5/krb/gic_pwd.c | 110 -----------------------------
src/lib/krb5/krb/t_expire_warn.c | 47 +++++++++----
src/lib/krb5/krb/t_expire_warn.py | 22 ++++--
5 files changed, 165 insertions(+), 135 deletions(-)
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 6355e6540..f8269fb17 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -7174,11 +7174,10 @@ typedef void
*
* Set a callback to receive password and account expiration times.
*
- * This option only applies to krb5_get_init_creds_password(). @a cb will be
- * invoked if and only if credentials are successfully acquired. The callback
- * will receive the @a context from the krb5_get_init_creds_password() call and
- * the @a data argument supplied with this API. The remaining arguments should
- * be interpreted as follows:
+ * @a cb will be invoked if and only if credentials are successfully acquired.
+ * The callback will receive the @a context from the calling function and the
+ * @a data argument supplied with this API. The remaining arguments should be
+ * interpreted as follows:
*
* If @a is_last_req is true, then the KDC reply contained last-req entries
* which unambiguously indicated the password expiration, account expiration,
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 870df62a1..cc0f70e83 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -1482,6 +1482,116 @@ accept_method_data(krb5_context context, krb5_init_creds_context ctx)
ctx->method_padata);
}
+/* Return the password expiry time indicated by enc_part2. Set *is_last_req
+ * if the information came from a last_req value. */
+static void
+get_expiry_times(krb5_enc_kdc_rep_part *enc_part2, krb5_timestamp *pw_exp,
+ krb5_timestamp *acct_exp, krb5_boolean *is_last_req)
+{
+ krb5_last_req_entry **last_req;
+ krb5_int32 lr_type;
+
+ *pw_exp = 0;
+ *acct_exp = 0;
+ *is_last_req = FALSE;
+
+ /* Look for last-req entries for password or account expiration. */
+ if (enc_part2->last_req) {
+ for (last_req = enc_part2->last_req; *last_req; last_req++) {
+ lr_type = (*last_req)->lr_type;
+ if (lr_type == KRB5_LRQ_ALL_PW_EXPTIME ||
+ lr_type == KRB5_LRQ_ONE_PW_EXPTIME) {
+ *is_last_req = TRUE;
+ *pw_exp = (*last_req)->value;
+ } else if (lr_type == KRB5_LRQ_ALL_ACCT_EXPTIME ||
+ lr_type == KRB5_LRQ_ONE_ACCT_EXPTIME) {
+ *is_last_req = TRUE;
+ *acct_exp = (*last_req)->value;
+ }
+ }
+ }
+
+ /* If we didn't find any, use the ambiguous key_exp field. */
+ if (*is_last_req == FALSE)
+ *pw_exp = enc_part2->key_exp;
+}
+
+/*
+ * 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_expire_callback_func expire_cb;
+ void *expire_data;
+ krb5_timestamp pw_exp, acct_exp, now;
+ krb5_boolean is_last_req;
+ krb5_deltat delta;
+ char ts[256], banner[1024];
+
+ if (as_reply == NULL || as_reply->enc_part2 == NULL)
+ return;
+
+ get_expiry_times(as_reply->enc_part2, &pw_exp, &acct_exp, &is_last_req);
+
+ k5_gic_opt_get_expire_cb(options, &expire_cb, &expire_data);
+ if (expire_cb != NULL) {
+ /* Invoke the expire callback and don't send prompter warnings. */
+ (*expire_cb)(context, expire_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 &&
+ (ts_after(now, pw_exp) || ts_delta(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 = ts_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);
+}
+
static krb5_error_code
init_creds_step_reply(krb5_context context,
krb5_init_creds_context ctx,
@@ -1693,6 +1803,8 @@ init_creds_step_reply(krb5_context context,
/* success */
ctx->complete = TRUE;
+ warn_pw_expiry(context, ctx->opt, ctx->prompter, ctx->prompter_data,
+ ctx->in_tkt_service, ctx->reply);
cleanup:
krb5_free_pa_data(context, kdc_padata);
diff --git a/src/lib/krb5/krb/gic_pwd.c b/src/lib/krb5/krb/gic_pwd.c
index 14ce23ba4..54e0a8ebe 100644
--- a/src/lib/krb5/krb/gic_pwd.c
+++ b/src/lib/krb5/krb/gic_pwd.c
@@ -133,113 +133,6 @@ krb5_init_creds_set_password(krb5_context context,
return 0;
}
-/* Return the password expiry time indicated by enc_part2. Set *is_last_req
- * if the information came from a last_req value. */
-static void
-get_expiry_times(krb5_enc_kdc_rep_part *enc_part2, krb5_timestamp *pw_exp,
- krb5_timestamp *acct_exp, krb5_boolean *is_last_req)
-{
- krb5_last_req_entry **last_req;
- krb5_int32 lr_type;
-
- *pw_exp = 0;
- *acct_exp = 0;
- *is_last_req = FALSE;
-
- /* Look for last-req entries for password or account expiration. */
- if (enc_part2->last_req) {
- for (last_req = enc_part2->last_req; *last_req; last_req++) {
- lr_type = (*last_req)->lr_type;
- if (lr_type == KRB5_LRQ_ALL_PW_EXPTIME ||
- lr_type == KRB5_LRQ_ONE_PW_EXPTIME) {
- *is_last_req = TRUE;
- *pw_exp = (*last_req)->value;
- } else if (lr_type == KRB5_LRQ_ALL_ACCT_EXPTIME ||
- lr_type == KRB5_LRQ_ONE_ACCT_EXPTIME) {
- *is_last_req = TRUE;
- *acct_exp = (*last_req)->value;
- }
- }
- }
-
- /* If we didn't find any, use the ambiguous key_exp field. */
- if (*is_last_req == FALSE)
- *pw_exp = enc_part2->key_exp;
-}
-
-/*
- * 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_expire_callback_func expire_cb;
- void *expire_data;
- krb5_timestamp pw_exp, acct_exp, now;
- krb5_boolean is_last_req;
- krb5_deltat delta;
- char ts[256], banner[1024];
-
- get_expiry_times(as_reply->enc_part2, &pw_exp, &acct_exp, &is_last_req);
-
- k5_gic_opt_get_expire_cb(options, &expire_cb, &expire_data);
- if (expire_cb != NULL) {
- /* Invoke the expire callback and don't send prompter warnings. */
- (*expire_cb)(context, expire_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 &&
- (ts_after(now, pw_exp) || ts_delta(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 = ts_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);
-}
-
/*
* Create a temporary options structure for getting a kadmin/changepw ticket,
* based on the appplication-specified options. Propagate all application
@@ -496,9 +389,6 @@ krb5_get_init_creds_password(krb5_context context,
goto cleanup;
cleanup:
- if (ret == 0)
- warn_pw_expiry(context, options, prompter, data, in_tkt_service,
- as_reply);
free(chpw_opts);
zapfree(gakpw.storage.data, gakpw.storage.length);
memset(pw0array, 0, sizeof(pw0array));
diff --git a/src/lib/krb5/krb/t_expire_warn.c b/src/lib/krb5/krb/t_expire_warn.c
index 1e59acba1..dc8dc8fb3 100644
--- a/src/lib/krb5/krb/t_expire_warn.c
+++ b/src/lib/krb5/krb/t_expire_warn.c
@@ -28,6 +28,13 @@
static int exp_dummy, prompt_dummy;
+static void
+check(krb5_error_code code)
+{
+ if (code != 0)
+ abort();
+}
+
static krb5_error_code
prompter_cb(krb5_context ctx, void *data, const char *name,
const char *banner, int num_prompts, krb5_prompt prompts[])
@@ -52,36 +59,48 @@ int
main(int argc, char **argv)
{
krb5_context ctx;
+ krb5_init_creds_context icctx;
krb5_get_init_creds_opt *opt;
char *user, *password, *service = NULL;
- krb5_boolean use_cb;
+ krb5_boolean use_cb, stepwise;
krb5_principal client;
krb5_creds creds;
- if (argc < 4) {
- fprintf(stderr, "Usage: %s username password {1|0} [service]\n",
+ if (argc < 5) {
+ fprintf(stderr, "Usage: %s username password {1|0} {1|0} [service]\n",
argv[0]);
return 1;
}
user = argv[1];
password = argv[2];
use_cb = atoi(argv[3]);
- if (argc >= 5)
- service = argv[4];
+ stepwise = atoi(argv[4]);
+ if (argc >= 6)
+ service = argv[5];
- assert(krb5_init_context(&ctx) == 0);
- assert(krb5_get_init_creds_opt_alloc(ctx, &opt) == 0);
+ check(krb5_init_context(&ctx));
+ check(krb5_get_init_creds_opt_alloc(ctx, &opt));
if (use_cb) {
- assert(krb5_get_init_creds_opt_set_expire_callback(ctx, opt, expire_cb,
- &exp_dummy) == 0);
+ check(krb5_get_init_creds_opt_set_expire_callback(ctx, opt, expire_cb,
+ &exp_dummy));
+ }
+ check(krb5_parse_name(ctx, user, &client));
+ if (stepwise) {
+ check(krb5_init_creds_init(ctx, client, prompter_cb, &prompt_dummy, 0,
+ opt, &icctx));
+ krb5_init_creds_set_password(ctx, icctx, password);
+ if (service != NULL)
+ check(krb5_init_creds_set_service(ctx, icctx, service));
+ check(krb5_init_creds_get(ctx, icctx));
+ krb5_init_creds_free(ctx, icctx);
+ } else {
+ check(krb5_get_init_creds_password(ctx, &creds, client, password,
+ prompter_cb, &prompt_dummy, 0,
+ service, opt));
+ krb5_free_cred_contents(ctx, &creds);
}
- assert(krb5_parse_name(ctx, user, &client) == 0);
- assert(krb5_get_init_creds_password(ctx, &creds, client, password,
- prompter_cb, &prompt_dummy, 0, service,
- opt) == 0);
krb5_get_init_creds_opt_free(ctx, opt);
krb5_free_principal(ctx, client);
- krb5_free_cred_contents(ctx, &creds);
krb5_free_context(ctx);
return 0;
}
diff --git a/src/lib/krb5/krb/t_expire_warn.py b/src/lib/krb5/krb/t_expire_warn.py
index 781f2728a..e163cc7e4 100755
--- a/src/lib/krb5/krb/t_expire_warn.py
+++ b/src/lib/krb5/krb/t_expire_warn.py
@@ -34,23 +34,33 @@ realm.run([kadminl, 'addprinc', '-pw', 'pass', '-pwexpire', '12 hours',
realm.run([kadminl, 'addprinc', '-pw', 'pass', '-pwexpire', '3 days', 'days'])
# Check for expected prompter warnings when no expire callback is used.
-output = realm.run(['./t_expire_warn', 'noexpire', 'pass', '0'])
+output = realm.run(['./t_expire_warn', 'noexpire', 'pass', '0', '0'])
if output:
fail('Unexpected output for noexpire')
-realm.run(['./t_expire_warn', 'minutes', 'pass', '0'],
+realm.run(['./t_expire_warn', 'minutes', 'pass', '0', '0'],
expected_msg=' less than one hour on ')
-realm.run(['./t_expire_warn', 'hours', 'pass', '0'], expected_msg=' hours on ')
-realm.run(['./t_expire_warn', 'days', 'pass', '0'], expected_msg=' days on ')
+realm.run(['./t_expire_warn', 'hours', 'pass', '0', '0'],
+ expected_msg=' hours on ')
+realm.run(['./t_expire_warn', 'days', 'pass', '0', '0'],
+ expected_msg=' days on ')
+# Try one case with the stepwise interface.
+realm.run(['./t_expire_warn', 'days', 'pass', '0', '1'],
+ expected_msg=' days on ')
# Check for expected expire callback behavior. These tests are
# carefully agnostic about whether the KDC supports last_req fields,
# and could be made more specific if last_req support is added.
-output = realm.run(['./t_expire_warn', 'noexpire', 'pass', '1'])
+output = realm.run(['./t_expire_warn', 'noexpire', 'pass', '1', '0'])
if 'password_expiration = 0\n' not in output or \
'account_expiration = 0\n' not in output or \
'is_last_req = ' not in output:
fail('Expected callback output not seen for noexpire')
-output = realm.run(['./t_expire_warn', 'days', 'pass', '1'])
+output = realm.run(['./t_expire_warn', 'days', 'pass', '1', '0'])
+if 'password_expiration = ' not in output or \
+ 'password_expiration = 0\n' in output:
+ fail('Expected non-zero password expiration not seen for days')
+# Try one case with the stepwise interface.
+output = realm.run(['./t_expire_warn', 'days', 'pass', '1', '1'])
if 'password_expiration = ' not in output or \
'password_expiration = 0\n' in output:
fail('Expected non-zero password expiration not seen for days')

View File

@ -0,0 +1,39 @@
From 5c1c391a80edd8ceb9e8bba9f7bdfb6639883ae6 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Tue, 24 Nov 2020 12:52:02 -0500
Subject: [PATCH] Document -k option in kvno(1) synopsis
becd1ad6830b526d08ddaf5b2b6f213154c6446c attempted to unify the
synopsis, option descriptions, and xusage(), but missed one option.
(cherry picked from commit d81e76d9ddab9e880bcf54eabf07119af91d28c7)
(cherry picked from commit 588d964f59356373353dfd31d4fdcba95e508385)
---
doc/user/user_commands/kvno.rst | 1 +
src/man/kvno.man | 1 +
2 files changed, 2 insertions(+)
diff --git a/doc/user/user_commands/kvno.rst b/doc/user/user_commands/kvno.rst
index 65c44e1c0..93a5132b2 100644
--- a/doc/user/user_commands/kvno.rst
+++ b/doc/user/user_commands/kvno.rst
@@ -9,6 +9,7 @@ SYNOPSIS
**kvno**
[**-c** *ccache*]
[**-e** *etype*]
+[**-k** *keytab*]
[**-q**]
[**-u** | **-S** *sname*]
[**-P**]
diff --git a/src/man/kvno.man b/src/man/kvno.man
index 22318324d..4e5b43b3b 100644
--- a/src/man/kvno.man
+++ b/src/man/kvno.man
@@ -35,6 +35,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
\fBkvno\fP
[\fB\-c\fP \fIccache\fP]
[\fB\-e\fP \fIetype\fP]
+[\fB\-k\fP \fIkeytab\fP]
[\fB\-q\fP]
[\fB\-u\fP | \fB\-S\fP \fIsname\fP]
[\fB\-P\fP]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,94 @@
From e2cc7a04f0dbfbf1a8bc6cd70f639c56a203af28 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 23 Mar 2020 19:10:03 -0400
Subject: [PATCH] Eliminate redundant PKINIT responder invocation
In pkinit_client_prep_questions(), only act if the input padata type
is KRB5_PADATA_PK_AS_REQ. Otherwise we will ask questions again when
the KDC issues a ticket.
Commit 7621d2f9a87214327ca3b2594e34dc7cea84596b (ticket 8242)
unintentionally changed the behavior of pkinit_load_fs_cert_and_key(),
causing pkinit_client_prep_questions() to do nothing on its first
call. Restore the original behavior of returning 0 when prompting is
deferred.
Modify the existing "FILE identity, password on key (responder)"
PKINIT test to check that the responder is only invoked once.
ticket: 8885
(cherry picked from commit f1286842ce7b9e507a4ce0a47f44ab361a98be63)
(cherry picked from commit 4a05805eb39ba088c07f782fb52a6538ec3f2db6)
---
src/plugins/preauth/pkinit/pkinit_clnt.c | 5 +++++
src/plugins/preauth/pkinit/pkinit_crypto_openssl.c | 13 +++++++------
src/tests/t_pkinit.py | 11 +++++++----
3 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index 2f0431991..9b991ffe0 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -897,6 +897,11 @@ pkinit_client_prep_questions(krb5_context context,
k5_json_object jval = NULL;
k5_json_number jflag = NULL;
+ /* Don't ask questions for the informational padata items or when the
+ * ticket is issued. */
+ if (pa_data->pa_type != KRB5_PADATA_PK_AS_REQ)
+ return 0;
+
if (!reqctx->identity_initialized) {
pkinit_client_profile(context, plgctx, reqctx, cb, rock,
&request->server->realm);
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index dd718c2be..dbb054378 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -4362,17 +4362,18 @@ pkinit_load_fs_cert_and_key(krb5_context context,
/* Load the certificate. */
retval = get_cert(certname, &x);
- if (retval != 0 || x == NULL) {
- retval = oerr(context, 0, _("Cannot read certificate file '%s'"),
+ if (retval) {
+ retval = oerr(context, retval, _("Cannot read certificate file '%s'"),
certname);
- goto cleanup;
}
+ if (retval || x == NULL)
+ goto cleanup;
/* Load the key. */
retval = get_key(context, id_cryptoctx, keyname, fsname, &y, password);
- if (retval != 0 || y == NULL) {
- retval = oerr(context, 0, _("Cannot read key file '%s'"), fsname);
+ if (retval)
+ retval = oerr(context, retval, _("Cannot read key file '%s'"), fsname);
+ if (retval || y == NULL)
goto cleanup;
- }
id_cryptoctx->creds[cindex] = malloc(sizeof(struct _pkinit_cred_info));
if (id_cryptoctx->creds[cindex] == NULL) {
diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py
index 69daf4987..ecd450e8a 100755
--- a/src/tests/t_pkinit.py
+++ b/src/tests/t_pkinit.py
@@ -248,10 +248,13 @@ realm.run(['./adata', realm.host_princ],
# supplied by the responder.
# Supply the response in raw form.
mark('FILE identity, password on key (responder)')
-realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % file_enc_identity,
- '-r', 'pkinit={"%s": "encrypted"}' % file_enc_identity,
- '-X', 'X509_user_identity=%s' % file_enc_identity,
- realm.user_princ])
+out = realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % file_enc_identity,
+ '-r', 'pkinit={"%s": "encrypted"}' % file_enc_identity,
+ '-X', 'X509_user_identity=%s' % file_enc_identity,
+ realm.user_princ])
+# Regression test for #8885 (password question asked twice).
+if out.count('OK: ') != 1:
+ fail('Wrong number of responder calls')
# Supply the response through the convenience API.
realm.run(['./responder', '-X', 'X509_user_identity=%s' % file_enc_identity,
'-p', '%s=%s' % (file_enc_identity, 'encrypted'), realm.user_princ])

View File

@ -0,0 +1,34 @@
From a68fba22588cc21dcd1dc28550529187dca58331 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 27 Oct 2023 00:44:53 -0400
Subject: [PATCH] End connection on KDC_ERR_SVC_UNAVAILABLE
In sendto_kdc.c:service_fds(), if a message handler indicates that a
message should be discarded, kill the connection so we don't continue
waiting on it for more data.
ticket: 7899
(cherry picked from commit ca80f64c786341d5871ae1de18142e62af64f7b9)
---
src/lib/krb5/os/sendto_kdc.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index d76e24ccf0..8e4fcd2a38 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -1435,7 +1435,10 @@ service_fds(krb5_context context, struct select_state *selstate,
if (msg_handler != NULL) {
krb5_data reply = make_data(state->in.buf, state->in.pos);
- stop = (msg_handler(context, &reply, msg_handler_data) != 0);
+ if (!msg_handler(context, &reply, msg_handler_data)) {
+ kill_conn(context, state, selstate);
+ stop = 0;
+ }
}
if (stop) {
--
2.44.0

View File

@ -1,31 +0,0 @@
From 3bfe632c7011c335362d78356232507d9ee26f73 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Wed, 14 Mar 2018 14:31:22 -0400
Subject: [PATCH] Exit with status 0 from kadmind
Typically, 0 denotes successful exit. In particular, init systems
will complain if another different value is returned. This presents a
problem for automated installation jobs which want to restart kadmind.
`service kadmin stop` typically sends SIGTERM, which is caught by
verto and passed to our handler. Besides cleanup, we then call
verto_break(), which causes the verto_run() event loop to return. The
weird return code has been present since the addition of the kadmin
code, which used a similar event model for signals.
(cherry picked from commit f970ad412aca36f8a7d3addb1cd4026ed22e5592)
---
src/kadmin/server/ovsec_kadmd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c
index aac4d4ffd..0a28b2384 100644
--- a/src/kadmin/server/ovsec_kadmd.c
+++ b/src/kadmin/server/ovsec_kadmd.c
@@ -559,5 +559,5 @@ main(int argc, char *argv[])
krb5_klog_close(context);
krb5_free_context(context);
- exit(2);
+ exit(0);
}

View File

@ -1,816 +0,0 @@
From 1d0c0db7755076834519fd02c271a78bbf26bb19 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 3 Jul 2018 01:20:50 -0400
Subject: [PATCH] Explicitly look for python2 in configure.in
The executable "python" has traditionally been Python 2, but is
becoming more ambiguous as operating systems transition towards Python
3. Look for "python2" in the path in preference to "python", and
check that what we found isn't Python 3.
Remove the "#!/usr/bin/python" headers at the start of Python test
scripts since we run them explicitly under python, not as executables.
Execute paste-kdcproxy.py via sys.executable in t_proxy.py so that it
doesn't need a #!/usr/bin/python header.
ticket: 8709 (new)
(cherry picked from commit 2bd410ecdb366083fe9b4e5f6ac4b741b624230b)
---
src/appl/gss-sample/t_gss_sample.py | 2 --
src/appl/user_user/t_user2user.py | 1 -
src/configure.in | 9 ++++++---
src/kadmin/dbutil/t_tdumputil.py | 2 --
src/kdc/t_bigreply.py | 1 -
src/kdc/t_emptytgt.py | 1 -
src/kdc/t_workers.py | 1 -
src/lib/kdb/t_stringattr.py | 1 -
src/lib/krad/t_daemon.py | 2 --
src/lib/krb5/ccache/t_cccol.py | 1 -
src/lib/krb5/krb/t_expire_warn.py | 2 --
src/lib/krb5/krb/t_in_ccache_patypes.py | 2 --
src/lib/krb5/krb/t_vfy_increds.py | 2 --
src/lib/krb5/os/t_discover_uri.py | 1 -
src/tests/gssapi/t_authind.py | 1 -
src/tests/gssapi/t_ccselect.py | 2 --
src/tests/gssapi/t_client_keytab.py | 1 -
src/tests/gssapi/t_enctypes.py | 1 -
src/tests/gssapi/t_export_cred.py | 1 -
src/tests/gssapi/t_gssapi.py | 1 -
src/tests/gssapi/t_s4u.py | 1 -
src/tests/jsonwalker.py | 2 --
src/tests/t_audit.py | 1 -
src/tests/t_authdata.py | 1 -
src/tests/t_bogus_kdc_req.py | 2 --
src/tests/t_ccache.py | 2 --
src/tests/t_certauth.py | 1 -
src/tests/t_changepw.py | 1 -
src/tests/t_crossrealm.py | 2 --
src/tests/t_cve-2012-1014.py | 2 --
src/tests/t_cve-2012-1015.py | 2 --
src/tests/t_cve-2013-1416.py | 2 --
src/tests/t_cve-2013-1417.py | 2 --
src/tests/t_dump.py | 1 -
src/tests/t_errmsg.py | 1 -
src/tests/t_etype_info.py | 1 -
src/tests/t_general.py | 1 -
src/tests/t_hooks.py | 1 -
src/tests/t_hostrealm.py | 1 -
src/tests/t_iprop.py | 2 --
src/tests/t_kadm5_auth.py | 1 -
src/tests/t_kadm5_hook.py | 1 -
src/tests/t_kadmin_acl.py | 1 -
src/tests/t_kadmin_parsing.py | 1 -
src/tests/t_kdb.py | 1 -
src/tests/t_kdb_locking.py | 2 --
src/tests/t_kdc_log.py | 2 --
src/tests/t_kdcpolicy.py | 1 -
src/tests/t_keydata.py | 1 -
src/tests/t_keyrollover.py | 1 -
src/tests/t_keytab.py | 1 -
src/tests/t_kprop.py | 1 -
src/tests/t_localauth.py | 1 -
src/tests/t_mkey.py | 1 -
src/tests/t_otp.py | 2 --
src/tests/t_pkinit.py | 1 -
src/tests/t_policy.py | 1 -
src/tests/t_preauth.py | 1 -
src/tests/t_princflags.py | 1 -
src/tests/t_proxy.py | 4 ++--
src/tests/t_pwqual.py | 1 -
src/tests/t_rdreq.py | 1 -
src/tests/t_referral.py | 1 -
src/tests/t_renew.py | 1 -
src/tests/t_renprinc.py | 2 --
src/tests/t_salt.py | 1 -
src/tests/t_sesskeynego.py | 1 -
src/tests/t_skew.py | 1 -
src/tests/t_sn2princ.py | 1 -
src/tests/t_spake.py | 1 -
src/tests/t_stringattr.py | 2 --
src/tests/t_tabdump.py | 1 -
src/tests/t_unlockiter.py | 1 -
src/tests/t_y2038.py | 1 -
src/util/paste-kdcproxy.py | 1 -
75 files changed, 8 insertions(+), 99 deletions(-)
diff --git a/src/appl/gss-sample/t_gss_sample.py b/src/appl/gss-sample/t_gss_sample.py
index 0299e4590..2f537823a 100755
--- a/src/appl/gss-sample/t_gss_sample.py
+++ b/src/appl/gss-sample/t_gss_sample.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# Copyright (C) 2010 by the Massachusetts Institute of Technology.
# All rights reserved.
#
diff --git a/src/appl/user_user/t_user2user.py b/src/appl/user_user/t_user2user.py
index 2a7d03f8d..2c054f181 100755
--- a/src/appl/user_user/t_user2user.py
+++ b/src/appl/user_user/t_user2user.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# If uuserver is not compiled under -DDEBUG, then set to 0
diff --git a/src/configure.in b/src/configure.in
index 08c63beca..3f45784b5 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -1098,13 +1098,16 @@ fi
AC_SUBST(HAVE_RUNTEST)
# For Python tests.
-AC_CHECK_PROG(PYTHON,python,python)
+AC_CHECK_PROG(PYTHON,python2,python2)
+if text x"$PYTHON" = x; then
+ AC_CHECK_PROG(PYTHON,python,python)
+fi
HAVE_PYTHON=no
if test x"$PYTHON" != x; then
# k5test.py requires python 2.4 (for the subprocess module).
# Some code needs python 2.5 (for syntax like conditional expressions).
- vercheck="import sys;sys.exit((sys.hexversion < 0x2050000) and 1 or 0)"
- if python -c "$vercheck"; then
+ wantver="(sys.hexversion >= 0x2050000 and sys.hexversion < 0x3000000)"
+ if "$PYTHON" -c "import sys; sys.exit(not $wantver and 1 or 0)"; then
HAVE_PYTHON=yes
fi
fi
diff --git a/src/kadmin/dbutil/t_tdumputil.py b/src/kadmin/dbutil/t_tdumputil.py
index 5d7ac38d2..52e356533 100755
--- a/src/kadmin/dbutil/t_tdumputil.py
+++ b/src/kadmin/dbutil/t_tdumputil.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
from k5test import *
from subprocess import *
diff --git a/src/kdc/t_bigreply.py b/src/kdc/t_bigreply.py
index 6bc9a8fe0..b6300154f 100644
--- a/src/kdc/t_bigreply.py
+++ b/src/kdc/t_bigreply.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Set the maximum UDP reply size very low, so that all replies go
diff --git a/src/kdc/t_emptytgt.py b/src/kdc/t_emptytgt.py
index 2d0432e33..c601c010c 100755
--- a/src/kdc/t_emptytgt.py
+++ b/src/kdc/t_emptytgt.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
realm = K5Realm(create_host=False)
diff --git a/src/kdc/t_workers.py b/src/kdc/t_workers.py
index 6dd4f6805..8de3f34d9 100755
--- a/src/kdc/t_workers.py
+++ b/src/kdc/t_workers.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
realm = K5Realm(start_kdc=False, create_host=False)
diff --git a/src/lib/kdb/t_stringattr.py b/src/lib/kdb/t_stringattr.py
index 085e179e4..93e2b0c01 100755
--- a/src/lib/kdb/t_stringattr.py
+++ b/src/lib/kdb/t_stringattr.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
realm = K5Realm(create_kdb=False)
diff --git a/src/lib/krad/t_daemon.py b/src/lib/krad/t_daemon.py
index dcda0050b..7d7a5d0c8 100755
--- a/src/lib/krad/t_daemon.py
+++ b/src/lib/krad/t_daemon.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-#
# Copyright 2013 Red Hat, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
diff --git a/src/lib/krb5/ccache/t_cccol.py b/src/lib/krb5/ccache/t_cccol.py
index f7f178564..1467512e2 100755
--- a/src/lib/krb5/ccache/t_cccol.py
+++ b/src/lib/krb5/ccache/t_cccol.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
realm = K5Realm(create_kdb=False)
diff --git a/src/lib/krb5/krb/t_expire_warn.py b/src/lib/krb5/krb/t_expire_warn.py
index aed39e399..781f2728a 100755
--- a/src/lib/krb5/krb/t_expire_warn.py
+++ b/src/lib/krb5/krb/t_expire_warn.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# Copyright (C) 2010 by the Massachusetts Institute of Technology.
# All rights reserved.
#
diff --git a/src/lib/krb5/krb/t_in_ccache_patypes.py b/src/lib/krb5/krb/t_in_ccache_patypes.py
index c04234064..b2812688c 100755
--- a/src/lib/krb5/krb/t_in_ccache_patypes.py
+++ b/src/lib/krb5/krb/t_in_ccache_patypes.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# Copyright (C) 2010,2012 by the Massachusetts Institute of Technology.
# All rights reserved.
#
diff --git a/src/lib/krb5/krb/t_vfy_increds.py b/src/lib/krb5/krb/t_vfy_increds.py
index c820cc690..b899308a8 100755
--- a/src/lib/krb5/krb/t_vfy_increds.py
+++ b/src/lib/krb5/krb/t_vfy_increds.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# Copyright (C) 2011 by the Massachusetts Institute of Technology.
# All rights reserved.
#
diff --git a/src/lib/krb5/os/t_discover_uri.py b/src/lib/krb5/os/t_discover_uri.py
index 278f98371..87bac1792 100644
--- a/src/lib/krb5/os/t_discover_uri.py
+++ b/src/lib/krb5/os/t_discover_uri.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
entries = ('URI _kerberos.TEST krb5srv::kkdcp:https://kdc1 1 1\n',
diff --git a/src/tests/gssapi/t_authind.py b/src/tests/gssapi/t_authind.py
index 84793beb6..af1741a23 100644
--- a/src/tests/gssapi/t_authind.py
+++ b/src/tests/gssapi/t_authind.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Test authentication indicators. Load the test preauth module so we
diff --git a/src/tests/gssapi/t_ccselect.py b/src/tests/gssapi/t_ccselect.py
index 3503f9269..cd62da231 100755
--- a/src/tests/gssapi/t_ccselect.py
+++ b/src/tests/gssapi/t_ccselect.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# Copyright (C) 2011 by the Massachusetts Institute of Technology.
# All rights reserved.
diff --git a/src/tests/gssapi/t_client_keytab.py b/src/tests/gssapi/t_client_keytab.py
index 2da87f45b..e474a27c7 100755
--- a/src/tests/gssapi/t_client_keytab.py
+++ b/src/tests/gssapi/t_client_keytab.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Set up a basic realm and a client keytab containing two user principals.
diff --git a/src/tests/gssapi/t_enctypes.py b/src/tests/gssapi/t_enctypes.py
index f513db2b5..ee43ff028 100755
--- a/src/tests/gssapi/t_enctypes.py
+++ b/src/tests/gssapi/t_enctypes.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Define some convenience abbreviations for enctypes we will see in
diff --git a/src/tests/gssapi/t_export_cred.py b/src/tests/gssapi/t_export_cred.py
index b98962788..89167bcc5 100755
--- a/src/tests/gssapi/t_export_cred.py
+++ b/src/tests/gssapi/t_export_cred.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Test gss_export_cred and gss_import_cred for initiator creds,
diff --git a/src/tests/gssapi/t_gssapi.py b/src/tests/gssapi/t_gssapi.py
index 6da5fceff..a7dda20fb 100755
--- a/src/tests/gssapi/t_gssapi.py
+++ b/src/tests/gssapi/t_gssapi.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Test krb5 negotiation under SPNEGO for all enctype configurations. Also
diff --git a/src/tests/gssapi/t_s4u.py b/src/tests/gssapi/t_s4u.py
index e4cd68469..fc9d9e8a4 100755
--- a/src/tests/gssapi/t_s4u.py
+++ b/src/tests/gssapi/t_s4u.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
realm = K5Realm(create_host=False, get_creds=False)
diff --git a/src/tests/jsonwalker.py b/src/tests/jsonwalker.py
index 265c69c70..942ca2db7 100644
--- a/src/tests/jsonwalker.py
+++ b/src/tests/jsonwalker.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
import sys
try:
import cjson
diff --git a/src/tests/t_audit.py b/src/tests/t_audit.py
index 00e96bfea..0f880edb2 100755
--- a/src/tests/t_audit.py
+++ b/src/tests/t_audit.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
conf = {'plugins': {'audit': {
diff --git a/src/tests/t_authdata.py b/src/tests/t_authdata.py
index 8a577b4b1..5cff80348 100644
--- a/src/tests/t_authdata.py
+++ b/src/tests/t_authdata.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Load the sample KDC authdata module.
diff --git a/src/tests/t_bogus_kdc_req.py b/src/tests/t_bogus_kdc_req.py
index b6208ca68..a101c0e10 100755
--- a/src/tests/t_bogus_kdc_req.py
+++ b/src/tests/t_bogus_kdc_req.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
import base64
import socket
from k5test import *
diff --git a/src/tests/t_ccache.py b/src/tests/t_ccache.py
index 61d549b7b..a913eb025 100755
--- a/src/tests/t_ccache.py
+++ b/src/tests/t_ccache.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# Copyright (C) 2011 by the Massachusetts Institute of Technology.
# All rights reserved.
diff --git a/src/tests/t_certauth.py b/src/tests/t_certauth.py
index e64a57b0d..9c7094525 100644
--- a/src/tests/t_certauth.py
+++ b/src/tests/t_certauth.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Skip this test if pkinit wasn't built.
diff --git a/src/tests/t_changepw.py b/src/tests/t_changepw.py
index 37fe4fce1..211cda6c3 100755
--- a/src/tests/t_changepw.py
+++ b/src/tests/t_changepw.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# This file is intended to cover any password-changing mechanism. For
diff --git a/src/tests/t_crossrealm.py b/src/tests/t_crossrealm.py
index 4d595dca6..09028bfa7 100755
--- a/src/tests/t_crossrealm.py
+++ b/src/tests/t_crossrealm.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# Copyright (C) 2011 by the Massachusetts Institute of Technology.
# All rights reserved.
#
diff --git a/src/tests/t_cve-2012-1014.py b/src/tests/t_cve-2012-1014.py
index e02162d6c..dcff95f6e 100755
--- a/src/tests/t_cve-2012-1014.py
+++ b/src/tests/t_cve-2012-1014.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
import base64
import socket
from k5test import *
diff --git a/src/tests/t_cve-2012-1015.py b/src/tests/t_cve-2012-1015.py
index e00c4dc90..28b1e619b 100755
--- a/src/tests/t_cve-2012-1015.py
+++ b/src/tests/t_cve-2012-1015.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
import base64
import socket
from k5test import *
diff --git a/src/tests/t_cve-2013-1416.py b/src/tests/t_cve-2013-1416.py
index 94fb6d5ef..8c4391a86 100755
--- a/src/tests/t_cve-2013-1416.py
+++ b/src/tests/t_cve-2013-1416.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
from k5test import *
realm = K5Realm()
diff --git a/src/tests/t_cve-2013-1417.py b/src/tests/t_cve-2013-1417.py
index c26930a30..ce47d21ca 100755
--- a/src/tests/t_cve-2013-1417.py
+++ b/src/tests/t_cve-2013-1417.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
from k5test import *
realm = K5Realm(realm='TEST')
diff --git a/src/tests/t_dump.py b/src/tests/t_dump.py
index 8a9462bd8..2cfeada6c 100755
--- a/src/tests/t_dump.py
+++ b/src/tests/t_dump.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
from filecmp import cmp
diff --git a/src/tests/t_errmsg.py b/src/tests/t_errmsg.py
index c9ae6637f..4aacf4e0a 100755
--- a/src/tests/t_errmsg.py
+++ b/src/tests/t_errmsg.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
realm = K5Realm(create_kdb=False)
diff --git a/src/tests/t_etype_info.py b/src/tests/t_etype_info.py
index b2eb0f7af..b12fb53c8 100644
--- a/src/tests/t_etype_info.py
+++ b/src/tests/t_etype_info.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
supported_enctypes = 'aes128-cts des3-cbc-sha1 rc4-hmac des-cbc-crc:afs3'
diff --git a/src/tests/t_general.py b/src/tests/t_general.py
index 91ad0cb8a..96ba8a4b0 100755
--- a/src/tests/t_general.py
+++ b/src/tests/t_general.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
for realm in multipass_realms(create_host=False):
diff --git a/src/tests/t_hooks.py b/src/tests/t_hooks.py
index 58dff3ae7..4fd3822e8 100755
--- a/src/tests/t_hooks.py
+++ b/src/tests/t_hooks.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Test that KDC send and recv hooks work correctly.
diff --git a/src/tests/t_hostrealm.py b/src/tests/t_hostrealm.py
index 224c067ef..256ba2a38 100755
--- a/src/tests/t_hostrealm.py
+++ b/src/tests/t_hostrealm.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
plugin = os.path.join(buildtop, "plugins", "hostrealm", "test",
diff --git a/src/tests/t_iprop.py b/src/tests/t_iprop.py
index 8e23cd5de..9cbeb3e68 100755
--- a/src/tests/t_iprop.py
+++ b/src/tests/t_iprop.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
import os
import re
diff --git a/src/tests/t_kadm5_auth.py b/src/tests/t_kadm5_auth.py
index ba4ab8ef1..6e0f42b08 100644
--- a/src/tests/t_kadm5_auth.py
+++ b/src/tests/t_kadm5_auth.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Create a realm with the welcomer and bouncer kadm5_auth test modules
diff --git a/src/tests/t_kadm5_hook.py b/src/tests/t_kadm5_hook.py
index c1c8c9419..32fab781d 100755
--- a/src/tests/t_kadm5_hook.py
+++ b/src/tests/t_kadm5_hook.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
plugin = os.path.join(buildtop, "plugins", "kadm5_hook", "test",
diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
index 42bdf423c..01a3eda29 100755
--- a/src/tests/t_kadmin_acl.py
+++ b/src/tests/t_kadmin_acl.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
import os
diff --git a/src/tests/t_kadmin_parsing.py b/src/tests/t_kadmin_parsing.py
index 8de387c64..bebb01488 100644
--- a/src/tests/t_kadmin_parsing.py
+++ b/src/tests/t_kadmin_parsing.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# This file contains tests for kadmin command parsing. Principal
diff --git a/src/tests/t_kdb.py b/src/tests/t_kdb.py
index 6e563b103..983cd93c8 100755
--- a/src/tests/t_kdb.py
+++ b/src/tests/t_kdb.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
import time
from itertools import imap
diff --git a/src/tests/t_kdb_locking.py b/src/tests/t_kdb_locking.py
index aac0a220f..b5afd6d23 100755
--- a/src/tests/t_kdb_locking.py
+++ b/src/tests/t_kdb_locking.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# This is a regression test for
# https://bugzilla.redhat.com/show_bug.cgi?id=586032 .
#
diff --git a/src/tests/t_kdc_log.py b/src/tests/t_kdc_log.py
index 8ddb7691b..1b14828de 100755
--- a/src/tests/t_kdc_log.py
+++ b/src/tests/t_kdc_log.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
from k5test import *
# Make a TGS request with an expired ticket.
diff --git a/src/tests/t_kdcpolicy.py b/src/tests/t_kdcpolicy.py
index 5b198bb43..a44adfdb5 100644
--- a/src/tests/t_kdcpolicy.py
+++ b/src/tests/t_kdcpolicy.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
from datetime import datetime
import re
diff --git a/src/tests/t_keydata.py b/src/tests/t_keydata.py
index 5c04a8523..b37233b21 100755
--- a/src/tests/t_keydata.py
+++ b/src/tests/t_keydata.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
realm = K5Realm(create_user=False, create_host=False)
diff --git a/src/tests/t_keyrollover.py b/src/tests/t_keyrollover.py
index bfd38914b..7c8d828f0 100755
--- a/src/tests/t_keyrollover.py
+++ b/src/tests/t_keyrollover.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
rollover_krb5_conf = {'libdefaults': {'allow_weak_crypto': 'true'}}
diff --git a/src/tests/t_keytab.py b/src/tests/t_keytab.py
index a48740ba5..228c36334 100755
--- a/src/tests/t_keytab.py
+++ b/src/tests/t_keytab.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
for realm in multipass_realms(create_user=False):
diff --git a/src/tests/t_kprop.py b/src/tests/t_kprop.py
index 39169675d..f352ec8d7 100755
--- a/src/tests/t_kprop.py
+++ b/src/tests/t_kprop.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
conf_slave = {'dbmodules': {'db': {'database_name': '$testdir/db.slave'}}}
diff --git a/src/tests/t_localauth.py b/src/tests/t_localauth.py
index aa625d038..ebc9cdfde 100755
--- a/src/tests/t_localauth.py
+++ b/src/tests/t_localauth.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Unfortunately, we can't reliably test the k5login module. We can control
diff --git a/src/tests/t_mkey.py b/src/tests/t_mkey.py
index 615cd91ca..48a533059 100755
--- a/src/tests/t_mkey.py
+++ b/src/tests/t_mkey.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
import random
import re
diff --git a/src/tests/t_otp.py b/src/tests/t_otp.py
index 9b18ff94b..0fd35d576 100755
--- a/src/tests/t_otp.py
+++ b/src/tests/t_otp.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-#
# Author: Nathaniel McCallum <npmccallum@redhat.com>
#
# Copyright (c) 2013 Red Hat, Inc.
diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py
index 0e964c689..850db4fdd 100755
--- a/src/tests/t_pkinit.py
+++ b/src/tests/t_pkinit.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Skip this test if pkinit wasn't built.
diff --git a/src/tests/t_policy.py b/src/tests/t_policy.py
index 26c4e466e..eb3865d7c 100755
--- a/src/tests/t_policy.py
+++ b/src/tests/t_policy.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
import re
diff --git a/src/tests/t_preauth.py b/src/tests/t_preauth.py
index 32e35b08b..f597c3d08 100644
--- a/src/tests/t_preauth.py
+++ b/src/tests/t_preauth.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Test that the kdcpreauth client_keyblock() callback matches the key
diff --git a/src/tests/t_princflags.py b/src/tests/t_princflags.py
index 6378ef94f..aa3660217 100755
--- a/src/tests/t_princflags.py
+++ b/src/tests/t_princflags.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
from princflags import *
import re
diff --git a/src/tests/t_proxy.py b/src/tests/t_proxy.py
index 4e86fce8f..ff1929bef 100755
--- a/src/tests/t_proxy.py
+++ b/src/tests/t_proxy.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Skip this test if we're missing proxy functionality or parts of the proxy.
@@ -62,7 +61,8 @@ def start_proxy(realm, keycertpem):
conf.write('kpasswd = kpasswd://localhost:%d\n' % (realm.portbase + 2))
conf.close()
realm.env['KDCPROXY_CONFIG'] = proxy_conf_path
- cmd = [proxy_exec_path, str(realm.server_port()), keycertpem]
+ cmd = [sys.executable, proxy_exec_path, str(realm.server_port()),
+ keycertpem]
return realm.start_server(cmd, sentinel='proxy server ready')
# Fail: untrusted issuer and hostname doesn't match.
diff --git a/src/tests/t_pwqual.py b/src/tests/t_pwqual.py
index 011110bd1..171805697 100755
--- a/src/tests/t_pwqual.py
+++ b/src/tests/t_pwqual.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
plugin = os.path.join(buildtop, "plugins", "pwqual", "test", "pwqual_test.so")
diff --git a/src/tests/t_rdreq.py b/src/tests/t_rdreq.py
index f67c34866..00cd5cbb4 100755
--- a/src/tests/t_rdreq.py
+++ b/src/tests/t_rdreq.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
conf = {'realms': {'$realm': {'supported_enctypes': 'aes256-cts aes128-cts'}}}
diff --git a/src/tests/t_referral.py b/src/tests/t_referral.py
index e12fdc2e9..2f29d5712 100755
--- a/src/tests/t_referral.py
+++ b/src/tests/t_referral.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Create a pair of realms, where KRBTEST1.COM can authenticate to
diff --git a/src/tests/t_renew.py b/src/tests/t_renew.py
index 034190c80..67b4182fd 100755
--- a/src/tests/t_renew.py
+++ b/src/tests/t_renew.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
from datetime import datetime
import re
diff --git a/src/tests/t_renprinc.py b/src/tests/t_renprinc.py
index cc780839a..46cbed441 100755
--- a/src/tests/t_renprinc.py
+++ b/src/tests/t_renprinc.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# Copyright (C) 2011 by the Massachusetts Institute of Technology.
# All rights reserved.
diff --git a/src/tests/t_salt.py b/src/tests/t_salt.py
index ddb1905ed..278911a22 100755
--- a/src/tests/t_salt.py
+++ b/src/tests/t_salt.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
import re
diff --git a/src/tests/t_sesskeynego.py b/src/tests/t_sesskeynego.py
index 732c306ea..448092387 100755
--- a/src/tests/t_sesskeynego.py
+++ b/src/tests/t_sesskeynego.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
import re
diff --git a/src/tests/t_skew.py b/src/tests/t_skew.py
index f2ae06695..36d5a95c5 100755
--- a/src/tests/t_skew.py
+++ b/src/tests/t_skew.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Create a realm with the KDC one hour in the past.
diff --git a/src/tests/t_sn2princ.py b/src/tests/t_sn2princ.py
index 19a0d2fa7..e2c85e665 100755
--- a/src/tests/t_sn2princ.py
+++ b/src/tests/t_sn2princ.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
offline = (len(args) > 0 and args[0] != "no")
diff --git a/src/tests/t_spake.py b/src/tests/t_spake.py
index 5b47e62d3..65af46d18 100644
--- a/src/tests/t_spake.py
+++ b/src/tests/t_spake.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# The name and number of each supported SPAKE group.
diff --git a/src/tests/t_stringattr.py b/src/tests/t_stringattr.py
index 5672a0f20..c2dc348e9 100755
--- a/src/tests/t_stringattr.py
+++ b/src/tests/t_stringattr.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
# Copyright (C) 2011 by the Massachusetts Institute of Technology.
# All rights reserved.
diff --git a/src/tests/t_tabdump.py b/src/tests/t_tabdump.py
index 066e48418..2a86136dd 100755
--- a/src/tests/t_tabdump.py
+++ b/src/tests/t_tabdump.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
import csv
diff --git a/src/tests/t_unlockiter.py b/src/tests/t_unlockiter.py
index 2a438e99a..603cf721d 100755
--- a/src/tests/t_unlockiter.py
+++ b/src/tests/t_unlockiter.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# Default KDB iteration is locked. Expect write lock failure unless
diff --git a/src/tests/t_y2038.py b/src/tests/t_y2038.py
index 02e946df4..42a4ff7ed 100644
--- a/src/tests/t_y2038.py
+++ b/src/tests/t_y2038.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
from k5test import *
# These tests will become much less important after the y2038 boundary
diff --git a/src/util/paste-kdcproxy.py b/src/util/paste-kdcproxy.py
index 1e56b8954..30467fd74 100755
--- a/src/util/paste-kdcproxy.py
+++ b/src/util/paste-kdcproxy.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
import kdcproxy
from paste import httpserver
import os

View File

@ -0,0 +1,258 @@
From b0372e31b81321a820204450a35c7633caf1b7dd Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 14 Jan 2022 02:05:58 -0500
Subject: [PATCH] Factor out PAC checksum verification
Reduce code repetition in PAC checksum handling by adding a helper
function. Remove the unnecessary prefix on several function names.
---
src/lib/krb5/krb/pac.c | 173 +++++++++++++----------------------------
1 file changed, 55 insertions(+), 118 deletions(-)
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 6eb23d8090..2f6ad4e1df 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -493,10 +493,8 @@ k5_pac_validate_client(krb5_context context,
}
static krb5_error_code
-k5_pac_zero_signature(krb5_context context,
- const krb5_pac pac,
- krb5_ui_4 type,
- krb5_data *data)
+zero_signature(krb5_context context, const krb5_pac pac, krb5_ui_4 type,
+ krb5_data *data)
{
PAC_INFO_BUFFER *buffer = NULL;
size_t i;
@@ -530,151 +528,89 @@ k5_pac_zero_signature(krb5_context context,
}
static krb5_error_code
-k5_pac_verify_server_checksum(krb5_context context,
- const krb5_pac pac,
- const krb5_keyblock *server)
+verify_checksum(krb5_context context, const krb5_pac pac, uint32_t buffer_type,
+ const krb5_keyblock *key, krb5_keyusage usage,
+ const krb5_data *data)
{
krb5_error_code ret;
- krb5_data pac_data; /* PAC with zeroed checksums */
+ krb5_data buffer;
+ krb5_cksumtype cksumtype;
krb5_checksum checksum;
- krb5_data checksum_data;
krb5_boolean valid;
- krb5_octet *p;
+ size_t cksumlen;
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
- &checksum_data);
+ ret = k5_pac_locate_buffer(context, pac, buffer_type, &buffer);
if (ret != 0)
return ret;
-
- if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
+ if (buffer.length < PAC_SIGNATURE_DATA_LENGTH)
return KRB5_BAD_MSIZE;
- p = (krb5_octet *)checksum_data.data;
- checksum.checksum_type = load_32_le(p);
- checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
- checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
- if (checksum.checksum_type == CKSUMTYPE_SHA1)
+ cksumtype = load_32_le(buffer.data);
+ if (buffer_type == KRB5_PAC_SERVER_CHECKSUM && cksumtype == CKSUMTYPE_SHA1)
return KRB5KDC_ERR_SUMTYPE_NOSUPP;
- if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
+ if (!krb5_c_is_keyed_cksum(cksumtype))
return KRB5KRB_ERR_GENERIC;
- pac_data.length = pac->data.length;
- pac_data.data = k5memdup(pac->data.data, pac->data.length, &ret);
- if (pac_data.data == NULL)
- return ret;
-
- /* Zero out both checksum buffers */
- ret = k5_pac_zero_signature(context, pac, KRB5_PAC_SERVER_CHECKSUM,
- &pac_data);
- if (ret != 0) {
- free(pac_data.data);
- return ret;
- }
-
- ret = k5_pac_zero_signature(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
- &pac_data);
- if (ret != 0) {
- free(pac_data.data);
+ /* There may be an RODCIdentifier trailer (see [MS-PAC] 2.8), so look up
+ * the length of the checksum by its type. */
+ ret = krb5_c_checksum_length(context, cksumtype, &cksumlen);
+ if (ret)
return ret;
- }
+ if (cksumlen > buffer.length - PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+ checksum.checksum_type = cksumtype;
+ checksum.length = cksumlen;
+ checksum.contents = (uint8_t *)buffer.data + PAC_SIGNATURE_DATA_LENGTH;
- ret = krb5_c_verify_checksum(context, server,
- KRB5_KEYUSAGE_APP_DATA_CKSUM,
- &pac_data, &checksum, &valid);
+ ret = krb5_c_verify_checksum(context, key, usage, data, &checksum, &valid);
+ return ret ? ret : (valid ? 0 : KRB5KRB_AP_ERR_MODIFIED);
+}
- free(pac_data.data);
+static krb5_error_code
+verify_server_checksum(krb5_context context, const krb5_pac pac,
+ const krb5_keyblock *server)
+{
+ krb5_error_code ret;
+ krb5_data copy; /* PAC with zeroed checksums */
- if (ret != 0) {
+ ret = krb5int_copy_data_contents(context, &pac->data, &copy);
+ if (ret)
return ret;
- }
- if (valid == FALSE)
- ret = KRB5KRB_AP_ERR_MODIFIED;
+ /* Zero out both checksum buffers */
+ ret = zero_signature(context, pac, KRB5_PAC_SERVER_CHECKSUM, &copy);
+ if (ret)
+ goto cleanup;
+ ret = zero_signature(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, &copy);
+ if (ret)
+ goto cleanup;
+
+ ret = verify_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM, server,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &copy);
+cleanup:
+ free(copy.data);
return ret;
}
static krb5_error_code
-k5_pac_verify_kdc_checksum(krb5_context context,
- const krb5_pac pac,
- const krb5_keyblock *privsvr)
+verify_kdc_checksum(krb5_context context, const krb5_pac pac,
+ const krb5_keyblock *privsvr)
{
krb5_error_code ret;
- krb5_data server_checksum, privsvr_checksum;
- krb5_checksum checksum;
- krb5_boolean valid;
- krb5_octet *p;
-
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
- &privsvr_checksum);
- if (ret != 0)
- return ret;
-
- if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
- return KRB5_BAD_MSIZE;
+ krb5_data server_checksum;
ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
&server_checksum);
- if (ret != 0)
+ if (ret)
return ret;
-
if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
return KRB5_BAD_MSIZE;
-
- p = (krb5_octet *)privsvr_checksum.data;
- checksum.checksum_type = load_32_le(p);
- checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
- checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
- if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
- return KRB5KRB_ERR_GENERIC;
-
server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
- ret = krb5_c_verify_checksum(context, privsvr,
- KRB5_KEYUSAGE_APP_DATA_CKSUM,
- &server_checksum, &checksum, &valid);
- if (ret != 0)
- return ret;
-
- if (valid == FALSE)
- ret = KRB5KRB_AP_ERR_MODIFIED;
-
- return ret;
-}
-
-static krb5_error_code
-verify_ticket_checksum(krb5_context context, const krb5_pac pac,
- const krb5_data *ticket, const krb5_keyblock *privsvr)
-{
- krb5_error_code ret;
- krb5_checksum checksum;
- krb5_data checksum_data;
- krb5_boolean valid;
- krb5_octet *p;
-
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_TICKET_CHECKSUM,
- &checksum_data);
- if (ret != 0)
- return KRB5KRB_AP_ERR_MODIFIED;
-
- if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
- return KRB5_BAD_MSIZE;
-
- p = (krb5_octet *)checksum_data.data;
- checksum.checksum_type = load_32_le(p);
- checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
- checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
- if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
- return KRB5KRB_ERR_GENERIC;
-
- ret = krb5_c_verify_checksum(context, privsvr,
- KRB5_KEYUSAGE_APP_DATA_CKSUM, ticket,
- &checksum, &valid);
- if (ret != 0)
- return ret;
-
- return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
+ return verify_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &server_checksum);
}
/* Per MS-PAC 2.8.3, tickets encrypted to TGS and password change principals
@@ -761,7 +697,8 @@ krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
if (ret)
goto cleanup;
- ret = verify_ticket_checksum(context, pac, recoded_tkt, privsvr);
+ ret = verify_checksum(context, pac, KRB5_PAC_TICKET_CHECKSUM, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, recoded_tkt);
if (ret)
goto cleanup;
}
@@ -804,13 +741,13 @@ krb5_pac_verify_ext(krb5_context context,
krb5_error_code ret;
if (server != NULL) {
- ret = k5_pac_verify_server_checksum(context, pac, server);
+ ret = verify_server_checksum(context, pac, server);
if (ret != 0)
return ret;
}
if (privsvr != NULL) {
- ret = k5_pac_verify_kdc_checksum(context, pac, privsvr);
+ ret = verify_kdc_checksum(context, pac, privsvr);
if (ret != 0)
return ret;
}
--
2.39.2

View File

@ -0,0 +1,105 @@
From 261b0ed68fb83c34c70679ae8452cae2dba7e4e3 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 29 Mar 2021 14:32:56 -0400
Subject: [PATCH] Fix KCM flag transmission for remove_cred
MIT krb5 uses low bits for KRB5_TC flags, while Heimdal uses high bits
so that the same flag word can also hold KRB5_GC flags. Add a mapping
function and send the Heimdal flag values when performing a
remove_cred operation.
ticket: 8995
(cherry picked from commit 11a82cf424f9c905bb73680c64524f087090d4ef)
(cherry picked from commit 04f0de4420508161ce439f262f2761ff51a07ab0)
(cherry picked from commit ddbb295dee2adcc6cec26944974420bba188f191)
---
src/include/kcm.h | 19 +++++++++++++++++++
src/lib/krb5/ccache/cc_kcm.c | 36 +++++++++++++++++++++++++++++++++++-
2 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/src/include/kcm.h b/src/include/kcm.h
index e4140c3a0..9b66f1cbd 100644
--- a/src/include/kcm.h
+++ b/src/include/kcm.h
@@ -56,8 +56,27 @@
* are marshalled as zero-terminated strings. Principals and credentials are
* marshalled in the v4 FILE ccache format. UUIDs are 16 bytes. UUID lists
* are not delimited, so nothing can come after them.
+ *
+ * Flag words must use Heimdal flag values, which are not the same as MIT krb5
+ * values for KRB5_GC and KRB5_TC constants. The same flag word may contain
+ * both kinds of flags in Heimdal, but not in MIT krb5. Defines for the
+ * applicable Heimdal flag values are given below using KCM_GC and KCM_TC
+ * prefixes.
*/
+#define KCM_GC_CACHED (1U << 0)
+
+#define KCM_TC_DONT_MATCH_REALM (1U << 31)
+#define KCM_TC_MATCH_KEYTYPE (1U << 30)
+#define KCM_TC_MATCH_SRV_NAMEONLY (1U << 29)
+#define KCM_TC_MATCH_FLAGS_EXACT (1U << 28)
+#define KCM_TC_MATCH_FLAGS (1U << 27)
+#define KCM_TC_MATCH_TIMES_EXACT (1U << 26)
+#define KCM_TC_MATCH_TIMES (1U << 25)
+#define KCM_TC_MATCH_AUTHDATA (1U << 24)
+#define KCM_TC_MATCH_2ND_TKT (1U << 23)
+#define KCM_TC_MATCH_IS_SKEY (1U << 22)
+
/* Opcodes without comments are currently unused in the MIT client
* implementation. */
typedef enum kcm_opcode {
diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c
index 197a10fba..4141140c3 100644
--- a/src/lib/krb5/ccache/cc_kcm.c
+++ b/src/lib/krb5/ccache/cc_kcm.c
@@ -110,6 +110,40 @@ map_invalid(krb5_error_code code)
KRB5_KCM_MALFORMED_REPLY : code;
}
+/*
+ * Map an MIT krb5 KRB5_TC flag word to the equivalent Heimdal flag word. Note
+ * that there is no MIT krb5 equivalent for Heimdal's KRB5_TC_DONT_MATCH_REALM
+ * (which is like KRB5_TC_MATCH_SRV_NAMEONLY but also applies to the client
+ * principal) and no Heimdal equivalent for MIT krb5's KRB5_TC_SUPPORTED_KTYPES
+ * (which matches against enctypes from the krb5_context rather than the
+ * matching cred).
+ */
+static inline krb5_flags
+map_tcflags(krb5_flags mitflags)
+{
+ krb5_flags heimflags = 0;
+
+ if (mitflags & KRB5_TC_MATCH_TIMES)
+ heimflags |= KCM_TC_MATCH_TIMES;
+ if (mitflags & KRB5_TC_MATCH_IS_SKEY)
+ heimflags |= KCM_TC_MATCH_IS_SKEY;
+ if (mitflags & KRB5_TC_MATCH_FLAGS)
+ heimflags |= KCM_TC_MATCH_FLAGS;
+ if (mitflags & KRB5_TC_MATCH_TIMES_EXACT)
+ heimflags |= KCM_TC_MATCH_TIMES_EXACT;
+ if (mitflags & KRB5_TC_MATCH_FLAGS_EXACT)
+ heimflags |= KCM_TC_MATCH_FLAGS_EXACT;
+ if (mitflags & KRB5_TC_MATCH_AUTHDATA)
+ heimflags |= KCM_TC_MATCH_AUTHDATA;
+ if (mitflags & KRB5_TC_MATCH_SRV_NAMEONLY)
+ heimflags |= KCM_TC_MATCH_SRV_NAMEONLY;
+ if (mitflags & KRB5_TC_MATCH_2ND_TKT)
+ heimflags |= KCM_TC_MATCH_2ND_TKT;
+ if (mitflags & KRB5_TC_MATCH_KTYPE)
+ heimflags |= KCM_TC_MATCH_KEYTYPE;
+ return heimflags;
+}
+
/* Begin a request for the given opcode. If cache is non-null, supply the
* cache name as a request parameter. */
static void
@@ -936,7 +970,7 @@ kcm_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
struct kcmreq req;
kcmreq_init(&req, KCM_OP_REMOVE_CRED, cache);
- k5_buf_add_uint32_be(&req.reqbuf, flags);
+ k5_buf_add_uint32_be(&req.reqbuf, map_tcflags(flags));
k5_marshal_mcred(&req.reqbuf, mcred);
ret = cache_call(context, cache, &req);
kcmreq_free(&req);

View File

@ -0,0 +1,64 @@
From 0bfe0b2bc0a8ee0e9a8cee26528030c16d4fd15f Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 11 May 2021 14:04:07 -0400
Subject: [PATCH] Fix KCM retrieval support for sssd
Commit 795ebba8c039be172ab93cd41105c73ffdba0fdb added a retrieval
handler using KCM_OP_RETRIEVE, falling back on the same error codes as
the previous KCM_OP_GET_CRED_LIST support. But sssd (as of 2.4)
returns KRB5_CC_NOSUPP instead of KRB5_CC_IO if it recognizes an
opcode but does not implement it. Add a helper function to recognize
all known unsupported-opcode error codes, and use it in kcm_retrieve()
and kcm_start_seq_get().
ticket: 8997
(cherry picked from commit da103e36e13f3c846bcddbe38dd518a21e5260a0)
(cherry picked from commit a5b2cff51808cd86fe8195e7ac074ecd25c3344d)
(cherry picked from commit 6a00fd149edd017ece894566771e2e9d4ba089f4)
---
src/lib/krb5/ccache/cc_kcm.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c
index b600c6f15..6a36cfdce 100644
--- a/src/lib/krb5/ccache/cc_kcm.c
+++ b/src/lib/krb5/ccache/cc_kcm.c
@@ -144,6 +144,20 @@ map_tcflags(krb5_flags mitflags)
return heimflags;
}
+/*
+ * Return true if code could indicate an unsupported operation. Heimdal's KCM
+ * returns KRB5_FCC_INTERNAL. sssd's KCM daemon (as of sssd 2.4) returns
+ * KRB5_CC_NO_SUPP if it recognizes the operation but does not implement it,
+ * and KRB5_CC_IO if it doesn't recognize the operation (which is unfortunate
+ * since it could also indicate a communication failure).
+ */
+static krb5_boolean
+unsupported_op_error(krb5_error_code code)
+{
+ return code == KRB5_FCC_INTERNAL || code == KRB5_CC_IO ||
+ code == KRB5_CC_NOSUPP;
+}
+
/* Begin a request for the given opcode. If cache is non-null, supply the
* cache name as a request parameter. */
static void
@@ -841,7 +855,7 @@ kcm_retrieve(krb5_context context, krb5_ccache cache, krb5_flags flags,
ret = cache_call(context, cache, &req);
/* Fall back to iteration if the server does not support retrieval. */
- if (ret == KRB5_FCC_INTERNAL || ret == KRB5_CC_IO) {
+ if (unsupported_op_error(ret)) {
ret = k5_cc_retrieve_cred_default(context, cache, flags, mcred,
cred_out);
goto cleanup;
@@ -922,7 +936,7 @@ kcm_start_seq_get(krb5_context context, krb5_ccache cache,
ret = kcmreq_get_cred_list(&req, &creds);
if (ret)
goto cleanup;
- } else if (ret == KRB5_FCC_INTERNAL || ret == KRB5_CC_IO) {
+ } else if (unsupported_op_error(ret)) {
/* Fall back to GET_CRED_UUID_LIST. */
kcmreq_free(&req);
kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache);

View File

@ -0,0 +1,47 @@
From 0a8dfc380fe3b210662ba1b1d452fcec2f84841b Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 3 Aug 2021 01:15:27 -0400
Subject: [PATCH] Fix KDC null deref on TGS inner body null server
After the KDC decodes a FAST inner body, it does not check for a null
server. Prior to commit 39548a5b17bbda9eeb63625a201cfd19b9de1c5b this
would typically result in an error from krb5_unparse_name(), but with
the addition of get_local_tgt() it results in a null dereference. Add
a null check.
Reported by Joseph Sutton of Catalyst.
CVE-2021-37750:
In MIT krb5 releases 1.14 and later, an authenticated attacker can
cause a null dereference in the KDC by sending a FAST TGS request with
no server field.
ticket: 9008 (new)
tags: pullup
target_version: 1.19-next
target_version: 1.18-next
(cherry picked from commit d775c95af7606a51bf79547a94fa52ddd1cb7f49)
(cherry picked from commit bb8fa495d00ccd931eec87a01b8920636cf7903e)
(cherry picked from commit dfe383f8251d0edc7e5e08ec5e4fdd9b7f902b2a)
---
src/kdc/do_tgs_req.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index 463a9c0dd..7c596a111 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -208,6 +208,11 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
status = "FIND_FAST";
goto cleanup;
}
+ if (sprinc == NULL) {
+ status = "NULL_SERVER";
+ errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto cleanup;
+ }
errcode = get_local_tgt(kdc_context, &sprinc->realm, header_server,
&local_tgt, &local_tgt_storage, &local_tgt_key);

View File

@ -0,0 +1,113 @@
From 4e8579f0a41b66ed8029f21a52082e1c27ab3996 Mon Sep 17 00:00:00 2001
From: Joseph Sutton <josephsutton@catalyst.net.nz>
Date: Wed, 7 Jul 2021 11:47:44 +1200
Subject: [PATCH] Fix KDC null deref on bad encrypted challenge
The function ec_verify() in src/kdc/kdc_preauth_ec.c contains a check
to avoid further processing if the armor key is NULL. However, this
check is bypassed by a call to k5memdup0() which overwrites retval
with 0 if the allocation succeeds. If the armor key is NULL, a call
to krb5_c_fx_cf2_simple() will then dereference it, resulting in a
crash. Add a check before the k5memdup0() call to avoid overwriting
retval.
CVE-2021-36222:
In MIT krb5 releases 1.16 and later, an unauthenticated attacker can
cause a null dereference in the KDC by sending a request containing a
PA-ENCRYPTED-CHALLENGE padata element without using FAST.
[ghudson@mit.edu: trimmed patch; added test case; edited commit
message]
(cherry picked from commit fc98f520caefff2e5ee9a0026fdf5109944b3562)
ticket: 9007
version_fixed: 1.18.4
(cherry picked from commit c4a406095b3ea4a67ae5b8ea586cbe9abdbae76f)
---
src/kdc/kdc_preauth_ec.c | 3 ++-
src/tests/Makefile.in | 1 +
src/tests/t_cve-2021-36222.py | 46 +++++++++++++++++++++++++++++++++++
3 files changed, 49 insertions(+), 1 deletion(-)
create mode 100644 src/tests/t_cve-2021-36222.py
diff --git a/src/kdc/kdc_preauth_ec.c b/src/kdc/kdc_preauth_ec.c
index 7e636b3f9..43a9902cc 100644
--- a/src/kdc/kdc_preauth_ec.c
+++ b/src/kdc/kdc_preauth_ec.c
@@ -87,7 +87,8 @@ ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
}
/* Check for a configured FAST ec auth indicator. */
- realmstr = k5memdup0(realm.data, realm.length, &retval);
+ if (retval == 0)
+ realmstr = k5memdup0(realm.data, realm.length, &retval);
if (realmstr != NULL)
retval = profile_get_string(context->profile, KRB5_CONF_REALMS,
realmstr,
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 3f88f1713..0ffbebf56 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -158,6 +158,7 @@ check-pytests: unlockiter s4u2self
$(RUNPYTEST) $(srcdir)/t_cve-2012-1015.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_cve-2013-1416.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_cve-2013-1417.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_cve-2021-36222.py $(PYTESTFLAGS)
$(RM) au.log
$(RUNPYTEST) $(srcdir)/t_audit.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/jsonwalker.py -d $(srcdir)/au_dict.json \
diff --git a/src/tests/t_cve-2021-36222.py b/src/tests/t_cve-2021-36222.py
new file mode 100644
index 000000000..57e04993b
--- /dev/null
+++ b/src/tests/t_cve-2021-36222.py
@@ -0,0 +1,46 @@
+import socket
+from k5test import *
+
+realm = K5Realm()
+
+# CVE-2021-36222 KDC null dereference on encrypted challenge preauth
+# without FAST
+
+s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+a = (hostname, realm.portbase)
+
+m = ('6A81A0' '30819D' # [APPLICATION 10] SEQUENCE
+ 'A103' '0201' '05' # [1] pvno = 5
+ 'A203' '0201' '0A' # [2] msg-type = 10
+ 'A30E' '300C' # [3] padata = SEQUENCE OF
+ '300A' # SEQUENCE
+ 'A104' '0202' '008A' # [1] padata-type = PA-ENCRYPTED-CHALLENGE
+ 'A202' '0400' # [2] padata-value = ""
+ 'A48180' '307E' # [4] req-body = SEQUENCE
+ 'A007' '0305' '0000000000' # [0] kdc-options = 0
+ 'A120' '301E' # [1] cname = SEQUENCE
+ 'A003' '0201' '01' # [0] name-type = NT-PRINCIPAL
+ 'A117' '3015' # [1] name-string = SEQUENCE-OF
+ '1B06' '6B7262746774' # krbtgt
+ '1B0B' '4B5242544553542E434F4D'
+ # KRBTEST.COM
+ 'A20D' '1B0B' '4B5242544553542E434F4D'
+ # [2] realm = KRBTEST.COM
+ 'A320' '301E' # [3] sname = SEQUENCE
+ 'A003' '0201' '01' # [0] name-type = NT-PRINCIPAL
+ 'A117' '3015' # [1] name-string = SEQUENCE-OF
+ '1B06' '6B7262746774' # krbtgt
+ '1B0B' '4B5242544553542E434F4D'
+ # KRBTEST.COM
+ 'A511' '180F' '31393934303631303036303331375A'
+ # [5] till = 19940610060317Z
+ 'A703' '0201' '00' # [7] nonce = 0
+ 'A808' '3006' # [8] etype = SEQUENCE OF
+ '020112' '020111') # aes256-cts aes128-cts
+
+s.sendto(bytes.fromhex(m), a)
+
+# Make sure kinit still works.
+realm.kinit(realm.user_princ, password('user'))
+
+success('CVE-2021-36222 regression test')

View File

@ -1,41 +0,0 @@
From 390c515e13dffc8c00b44623cba47e27c2f20cf7 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 27 Mar 2018 10:36:05 -0400
Subject: [PATCH] Fix SPAKE memory leak
In the NIST group implementations, ossl_fini() needs to free the
groupdata container as well as its fields. Also in
spake_kdc.c:parse_data(), initialize the magic field of the resulting
data object to avoid a harmless uninitialized memory copy.
ticket: 8647
(cherry picked from commit 70b88b8018658e052d6eabf06f8fdad17fbe993c)
---
src/plugins/preauth/spake/openssl.c | 1 +
src/plugins/preauth/spake/spake_kdc.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/plugins/preauth/spake/openssl.c b/src/plugins/preauth/spake/openssl.c
index b821a9158..f2e4b53ec 100644
--- a/src/plugins/preauth/spake/openssl.c
+++ b/src/plugins/preauth/spake/openssl.c
@@ -69,6 +69,7 @@ ossl_fini(groupdata *gd)
EC_POINT_free(gd->N);
BN_CTX_free(gd->ctx);
BN_free(gd->order);
+ free(gd);
}
static krb5_error_code
diff --git a/src/plugins/preauth/spake/spake_kdc.c b/src/plugins/preauth/spake/spake_kdc.c
index c1723ebaf..59e88409e 100644
--- a/src/plugins/preauth/spake/spake_kdc.c
+++ b/src/plugins/preauth/spake/spake_kdc.c
@@ -75,6 +75,7 @@ parse_data(struct k5input *in, krb5_data *out)
{
out->length = k5_input_get_uint32_be(in);
out->data = (char *)k5_input_get_bytes(in, out->length);
+ out->magic = KV5M_DATA;
}
/* Parse a received cookie into its components. The pointers stored in the

View File

@ -0,0 +1,45 @@
From 058dfbaed97c8e09ac4f3f7a1655b64ab3cf0144 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 21 Jul 2021 13:44:30 -0400
Subject: [PATCH] Fix defcred leak in krb5 gss_inquire_cred()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Commit 1cd2821c19b2b95e39d5fc2f451a035585a40fa5 altered the memory
management of krb5_gss_inquire_cred(), introducing defcred to act as
an owner pointer when the function must acquire a default credential.
The commit neglected to update the code to release the default cred
along the successful path. The old code does not trigger because
cred_handle is now reassigned, so the default credential is leaked.
Reported by Pavel Březina.
(a minimal alternative to commit 593e16448e1af23eef74689afe06a7bcc86e79c7)
ticket: 9016
version_fixed: 1.18.4
(cherry picked from commit b92be484630b38e26f5ee4bd67973fbd7627009c)
---
src/lib/gssapi/krb5/inq_cred.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c
index a8f2541102..cd8384d08c 100644
--- a/src/lib/gssapi/krb5/inq_cred.c
+++ b/src/lib/gssapi/krb5/inq_cred.c
@@ -197,9 +197,7 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
mechs = GSS_C_NO_OID_SET;
}
- if (cred_handle == GSS_C_NO_CREDENTIAL)
- krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred);
-
+ krb5_gss_release_cred(minor_status, &defcred);
krb5_free_context(context);
*minor_status = 0;
return((lifetime == 0)?GSS_S_CREDENTIALS_EXPIRED:GSS_S_COMPLETE);
--
2.44.0

View File

@ -1,92 +0,0 @@
From 8b898badbe8051270c6da96f5c15f3bc8b6d974e Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Fri, 26 Jan 2018 11:47:50 -0500
Subject: [PATCH] Fix hex conversion of PKINIT certid strings
When parsing a PKCS11 token specification, correctly convert from hex
to binary instead of using OpenSSL bignum functions (which would strip
leading zeros).
[ghudson@mit.edu: made hex_string_to_bin() a bit less verbose; wrote
commit message]
ticket: 8636
(cherry picked from commit 63e8b8142fd7b3931a7bf2d6448978ca536bafc0)
---
.../preauth/pkinit/pkinit_crypto_openssl.c | 55 +++++++++++++++----
1 file changed, 44 insertions(+), 11 deletions(-)
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index 2064eb7bd..eb2953fe1 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -4616,6 +4616,43 @@ reassemble_pkcs11_name(pkinit_identity_opts *idopts)
return ret;
}
+static int
+hex_string_to_bin(const char *str, int *bin_len_out, CK_BYTE **bin_out)
+{
+ size_t str_len, i;
+ CK_BYTE *bin;
+ char *endptr, tmp[3] = { '\0', '\0', '\0' };
+ long val;
+
+ *bin_len_out = 0;
+ *bin_out = NULL;
+
+ str_len = strlen(str);
+ if (str_len % 2 != 0)
+ return EINVAL;
+ bin = malloc(str_len / 2);
+ if (bin == NULL)
+ return ENOMEM;
+
+ errno = 0;
+ for (i = 0; i < str_len / 2; i++) {
+ tmp[0] = str[i * 2];
+ tmp[1] = str[i * 2 + 1];
+
+ val = strtol(tmp, &endptr, 16);
+ if (val < 0 || val > 255 || errno != 0 || endptr != &tmp[2]) {
+ free(bin);
+ return EINVAL;
+ }
+
+ bin[i] = (CK_BYTE)val;
+ }
+
+ *bin_len_out = str_len / 2;
+ *bin_out = bin;
+ return 0;
+}
+
static krb5_error_code
pkinit_get_certs_pkcs11(krb5_context context,
pkinit_plg_crypto_context plg_cryptoctx,
@@ -4658,18 +4695,14 @@ pkinit_get_certs_pkcs11(krb5_context context,
}
/* Convert the ascii cert_id string into a binary blob */
if (idopts->cert_id_string != NULL) {
- BIGNUM *bn = NULL;
- BN_hex2bn(&bn, idopts->cert_id_string);
- if (bn == NULL)
- return ENOMEM;
- id_cryptoctx->cert_id_len = BN_num_bytes(bn);
- id_cryptoctx->cert_id = malloc((size_t) id_cryptoctx->cert_id_len);
- if (id_cryptoctx->cert_id == NULL) {
- BN_free(bn);
- return ENOMEM;
+ r = hex_string_to_bin(idopts->cert_id_string,
+ &id_cryptoctx->cert_id_len,
+ &id_cryptoctx->cert_id);
+ if (r != 0) {
+ pkiDebug("Failed to convert certid string [%s]\n",
+ idopts->cert_id_string);
+ return r;
}
- BN_bn2bin(bn, id_cryptoctx->cert_id);
- BN_free(bn);
}
id_cryptoctx->slotid = idopts->slotid;
id_cryptoctx->pkcs11_method = 1;

View File

@ -0,0 +1,106 @@
From d2477aa606ad590ca4097941bb6c2e1955b2a8c8 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 17 Oct 2022 20:25:11 -0400
Subject: [PATCH] Fix integer overflows in PAC parsing
In krb5_parse_pac(), check for buffer counts large enough to threaten
integer overflow in the header length and memory length calculations.
Avoid potential integer overflows when checking the length of each
buffer.
CVE-2022-42898:
In MIT krb5 releases 1.8 and later, an authenticated attacker may be
able to cause a KDC or kadmind process to crash by reading beyond the
bounds of allocated memory, creating a denial of service. A
privileged attacker may similarly be able to cause a Kerberos or GSS
application service to crash. On 32-bit platforms, an attacker can
also cause insufficient memory to be allocated for the result,
potentially leading to remote code execution in a KDC, kadmind, or GSS
or Kerberos application server process. An attacker with the
privileges of a cross-realm KDC may be able to extract secrets from
the KDC process's memory by having them copied into the PAC of a new
ticket.
ticket: 9074 (new)
tags: pullup
target_version: 1.20-next
target_version: 1.19-next
---
src/lib/krb5/krb/pac.c | 9 +++++++--
src/lib/krb5/krb/t_pac.c | 18 ++++++++++++++++++
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 950beda657..1b9ef12276 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -27,6 +27,8 @@
#include "k5-int.h"
#include "authdata.h"
+#define MAX_BUFFERS 4096
+
/* draft-brezak-win2k-krb-authz-00 */
/*
@@ -316,6 +318,9 @@ krb5_pac_parse(krb5_context context,
if (version != 0)
return EINVAL;
+ if (cbuffers < 1 || cbuffers > MAX_BUFFERS)
+ return ERANGE;
+
header_len = PACTYPE_LENGTH + (cbuffers * PAC_INFO_BUFFER_LENGTH);
if (len < header_len)
return ERANGE;
@@ -348,8 +353,8 @@ krb5_pac_parse(krb5_context context,
krb5_pac_free(context, pac);
return EINVAL;
}
- if (buffer->Offset < header_len ||
- buffer->Offset + buffer->cbBufferSize > len) {
+ if (buffer->Offset < header_len || buffer->Offset > len ||
+ buffer->cbBufferSize > len - buffer->Offset) {
krb5_pac_free(context, pac);
return ERANGE;
}
diff --git a/src/lib/krb5/krb/t_pac.c b/src/lib/krb5/krb/t_pac.c
index ee47152ee4..ccd165380d 100644
--- a/src/lib/krb5/krb/t_pac.c
+++ b/src/lib/krb5/krb/t_pac.c
@@ -431,6 +431,16 @@ static const unsigned char s4u_pac_ent_xrealm[] = {
0x8a, 0x81, 0x9c, 0x9c, 0x00, 0x00, 0x00, 0x00
};
+static const unsigned char fuzz1[] = {
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0xff, 0xff, 0xff, 0x00, 0x00, 0xf5
+};
+
+static const unsigned char fuzz2[] = {
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x20
+};
+
static const char *s4u_principal = "w2k8u@ACME.COM";
static const char *s4u_enterprise = "w2k8u@abc@ACME.COM";
@@ -646,6 +656,14 @@ main(int argc, char **argv)
krb5_free_principal(context, sep);
}
+ /* Check problematic PACs found by fuzzing. */
+ ret = krb5_pac_parse(context, fuzz1, sizeof(fuzz1), &pac);
+ if (!ret)
+ err(context, ret, "krb5_pac_parse should have failed");
+ ret = krb5_pac_parse(context, fuzz2, sizeof(fuzz2), &pac);
+ if (!ret)
+ err(context, ret, "krb5_pac_parse should have failed");
+
/*
* Test empty free
*/
--
2.37.3

View File

@ -1,35 +0,0 @@
From 43cf653d21d931b792b36c7e6e4cfab3a6236bef Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 25 Jul 2018 11:50:02 -0400
Subject: [PATCH] Fix k5test prompts for Python 3
With Python 3, sys.stdout.write() of a partial line followed by
sys.stdin.readline() does not display the partial line. Add explicit
flushes to make prompts visible in k5test.py.
ticket: 8710
(cherry picked from commit 297535b72177dcced036b78107e9d0e37781c7a3)
---
src/util/k5test.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/util/k5test.py b/src/util/k5test.py
index 81fac3063..e4f99b211 100644
--- a/src/util/k5test.py
+++ b/src/util/k5test.py
@@ -457,6 +457,7 @@ def _onexit():
if _debug or _stop_before or _stop_after or _shell_before or _shell_after:
# Wait before killing daemons in case one is being debugged.
sys.stdout.write('*** Press return to kill daemons and exit script: ')
+ sys.stdout.flush()
sys.stdin.readline()
for proc in _daemons:
os.kill(proc.pid, signal.SIGTERM)
@@ -658,6 +659,7 @@ def _valgrind(args):
def _stop_or_shell(stop, shell, env, ind):
if (_match_cmdnum(stop, ind)):
sys.stdout.write('*** [%d] Waiting for return: ' % ind)
+ sys.stdout.flush()
sys.stdin.readline()
if (_match_cmdnum(shell, ind)):
output('*** [%d] Spawning shell\n' % ind, True)

View File

@ -0,0 +1,60 @@
From 7a87189f7bdabc144e22d4caa6a0785a06416d8f Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 24 Jul 2020 16:05:24 -0400
Subject: [PATCH] Fix leak in KERB_AP_OPTIONS_CBT server support
In check_cbt(), use a local variable to hold the retrieved authdata
list, and free it before returning.
ticket: 8900
(cherry picked from commit bf2ddff13c178e0c291f8fb382b040080d159e4f)
(cherry picked from commit 044e2209586fd1935d9a637df76d52f48c4f3e6e)
---
src/lib/gssapi/krb5/accept_sec_context.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index 175a24c4e..3d5b84b15 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -433,27 +433,30 @@ static const uint8_t null_cb[CB_MD5_LEN];
/* Look for AP_OPTIONS in authdata. If present and the options include
* KERB_AP_OPTIONS_CBT, set *cbt_out to true. */
static krb5_error_code
-check_cbt(krb5_context context, krb5_authdata **authdata,
+check_cbt(krb5_context context, krb5_authdata *const *authdata,
krb5_boolean *cbt_out)
{
krb5_error_code code;
+ krb5_authdata **ad;
uint32_t ad_ap_options;
const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
*cbt_out = FALSE;
code = krb5_find_authdata(context, NULL, authdata,
- KRB5_AUTHDATA_AP_OPTIONS, &authdata);
- if (code || authdata == NULL)
+ KRB5_AUTHDATA_AP_OPTIONS, &ad);
+ if (code || ad == NULL)
return code;
- if (authdata[1] != NULL || authdata[0]->length != 4)
- return KRB5KRB_AP_ERR_MSG_TYPE;
+ if (ad[1] != NULL || ad[0]->length != 4) {
+ code = KRB5KRB_AP_ERR_MSG_TYPE;
+ } else {
+ ad_ap_options = load_32_le(ad[0]->contents);
+ if (ad_ap_options & KERB_AP_OPTIONS_CBT)
+ *cbt_out = TRUE;
+ }
- ad_ap_options = load_32_le(authdata[0]->contents);
- if (ad_ap_options & KERB_AP_OPTIONS_CBT)
- *cbt_out = TRUE;
-
- return 0;
+ krb5_free_authdata(context, ad);
+ return code;
}
/*

View File

@ -1,48 +0,0 @@
From 59a28991e15496e6f9cf867c32dc18e7e1062f59 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 15 Mar 2018 20:27:30 -0400
Subject: [PATCH] Fix read overflow in KDC sort_pa_data()
sort_pa_data() could read past the end of pa_order if all preauth
systems in the table have the PA_REPLACES_KEY flag, causing a
dereference of preauth_systems[-1]. This situation became possible
after commit fea1a488924faa3938ef723feaa1ff12d22a91ff with the
elimination of static_preauth_systems; before that there were always
table entries which did not have PA_REPLACES_KEY set.
Fix this bug by removing the loop to count n_key_replacers, and
instead get the count from the prior loop by stopping once we move all
of the key-replacing modules to the front.
(cherry picked from commit b38e318cea18fd65647189eed64aef83bf1cb772)
---
src/kdc/kdc_preauth.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 80b130222..62ff9a8a7 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -663,17 +663,18 @@ sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
break;
}
}
+ /* If we didn't find one, we have moved all of the key-replacing
+ * modules, and i is the count of those modules. */
+ if (j == n_repliers)
+ break;
}
+ n_key_replacers = i;
if (request->padata != NULL) {
/* Now reorder the subset of modules which replace the key,
* bubbling those which handle pa_data types provided by the
* client ahead of the others.
*/
- for (i = 0; preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY; i++) {
- continue;
- }
- n_key_replacers = i;
for (i = 0; i < n_key_replacers; i++) {
if (pa_list_includes(request->padata,
preauth_systems[pa_order[i]].type))

View File

@ -1,43 +0,0 @@
From e405f42b532e377e7e3d654313a07f8c11f48f9a Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 3 Jan 2018 12:06:08 -0500
Subject: [PATCH] Fix securid_sam2 preauth for non-default salt
When looking up the client long-term key, look for any salt type, not
just the default salt type.
ticket: 8629
(cherry picked from commit a2339099ad13c84de0843fd04d0ba612fc194a1e)
---
src/plugins/preauth/securid_sam2/grail.c | 3 +--
src/plugins/preauth/securid_sam2/securid2.c | 3 +--
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/plugins/preauth/securid_sam2/grail.c b/src/plugins/preauth/securid_sam2/grail.c
index 18d48f924..48b61b0d1 100644
--- a/src/plugins/preauth/securid_sam2/grail.c
+++ b/src/plugins/preauth/securid_sam2/grail.c
@@ -213,8 +213,7 @@ verify_grail_data(krb5_context context, krb5_db_entry *client,
return KRB5KDC_ERR_PREAUTH_FAILED;
ret = krb5_dbe_find_enctype(context, client,
- sr2->sam_enc_nonce_or_sad.enctype,
- KRB5_KDB_SALTTYPE_NORMAL,
+ sr2->sam_enc_nonce_or_sad.enctype, -1,
sr2->sam_enc_nonce_or_sad.kvno,
&client_key_data);
if (ret)
diff --git a/src/plugins/preauth/securid_sam2/securid2.c b/src/plugins/preauth/securid_sam2/securid2.c
index ca99ce3ef..363e17a10 100644
--- a/src/plugins/preauth/securid_sam2/securid2.c
+++ b/src/plugins/preauth/securid_sam2/securid2.c
@@ -313,8 +313,7 @@ verify_securid_data_2(krb5_context context, krb5_db_entry *client,
}
retval = krb5_dbe_find_enctype(context, client,
- sr2->sam_enc_nonce_or_sad.enctype,
- KRB5_KDB_SALTTYPE_NORMAL,
+ sr2->sam_enc_nonce_or_sad.enctype, -1,
sr2->sam_enc_nonce_or_sad.kvno,
&client_key_data);
if (retval) {

View File

@ -1,133 +0,0 @@
From 617d153bb32d0bd7db33ccec21043d1113651f3a Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Wed, 18 Apr 2018 14:13:28 -0400
Subject: [PATCH] Fix segfault in finish_dispatch()
dispatch() doesn't necessarily initialize state->active_realm which
led to an explicit NULL dereference in finish_dispatch().
Additionally, fix make_too_big_error() so that it won't subsequently
dereference state->active_realm.
tags: pullup
target_version: 1.16-next
target_version: 1.15-next
---
src/kdc/dispatch.c | 79 ++++++++++++++++++++++++----------------------
1 file changed, 42 insertions(+), 37 deletions(-)
diff --git a/src/kdc/dispatch.c b/src/kdc/dispatch.c
index 3ed5176a8..fb3686c98 100644
--- a/src/kdc/dispatch.c
+++ b/src/kdc/dispatch.c
@@ -35,9 +35,6 @@
static krb5_int32 last_usec = 0, last_os_random = 0;
-static krb5_error_code make_too_big_error(kdc_realm_t *kdc_active_realm,
- krb5_data **out);
-
struct dispatch_state {
loop_respond_fn respond;
void *arg;
@@ -47,6 +44,41 @@ struct dispatch_state {
krb5_context kdc_err_context;
};
+
+static krb5_error_code
+make_too_big_error(krb5_context context, krb5_principal tgsprinc,
+ krb5_data **out)
+{
+ krb5_error errpkt;
+ krb5_error_code retval;
+ krb5_data *scratch;
+
+ *out = NULL;
+ memset(&errpkt, 0, sizeof(errpkt));
+
+ retval = krb5_us_timeofday(context, &errpkt.stime, &errpkt.susec);
+ if (retval)
+ return retval;
+ errpkt.error = KRB_ERR_RESPONSE_TOO_BIG;
+ errpkt.server = tgsprinc;
+ errpkt.client = NULL;
+ errpkt.text.length = 0;
+ errpkt.text.data = 0;
+ errpkt.e_data.length = 0;
+ errpkt.e_data.data = 0;
+ scratch = malloc(sizeof(*scratch));
+ if (scratch == NULL)
+ return ENOMEM;
+ retval = krb5_mk_error(context, &errpkt, scratch);
+ if (retval) {
+ free(scratch);
+ return retval;
+ }
+
+ *out = scratch;
+ return 0;
+}
+
static void
finish_dispatch(struct dispatch_state *state, krb5_error_code code,
krb5_data *response)
@@ -54,12 +86,17 @@ finish_dispatch(struct dispatch_state *state, krb5_error_code code,
loop_respond_fn oldrespond = state->respond;
void *oldarg = state->arg;
kdc_realm_t *kdc_active_realm = state->active_realm;
+ krb5_principal tgsprinc = NULL;
+
+ if (kdc_active_realm != NULL)
+ tgsprinc = kdc_active_realm->realm_tgsprinc;
if (state->is_tcp == 0 && response &&
response->length > (unsigned int)max_dgram_reply_size) {
- krb5_free_data(kdc_context, response);
+ krb5_free_data(state->kdc_err_context, response);
response = NULL;
- code = make_too_big_error(kdc_active_realm, &response);
+ code = make_too_big_error(state->kdc_err_context, tgsprinc,
+ &response);
if (code)
krb5_klog_syslog(LOG_ERR, "error constructing "
"KRB_ERR_RESPONSE_TOO_BIG error: %s",
@@ -208,38 +245,6 @@ done:
finish_dispatch_cache(state, retval, response);
}
-static krb5_error_code
-make_too_big_error(kdc_realm_t *kdc_active_realm, krb5_data **out)
-{
- krb5_error errpkt;
- krb5_error_code retval;
- krb5_data *scratch;
-
- *out = NULL;
- memset(&errpkt, 0, sizeof(errpkt));
-
- retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec);
- if (retval)
- return retval;
- errpkt.error = KRB_ERR_RESPONSE_TOO_BIG;
- errpkt.server = tgs_server;
- errpkt.client = NULL;
- errpkt.text.length = 0;
- errpkt.text.data = 0;
- errpkt.e_data.length = 0;
- errpkt.e_data.data = 0;
- scratch = malloc(sizeof(*scratch));
- if (scratch == NULL)
- return ENOMEM;
- retval = krb5_mk_error(kdc_context, &errpkt, scratch);
- if (retval) {
- free(scratch);
- return retval;
- }
-
- *out = scratch;
- return 0;
-}
krb5_context get_context(void *handle)
{

View File

@ -1,81 +0,0 @@
From eb60404564852a262d4082c3e38086742afb1bd9 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Mon, 16 Jul 2018 16:44:01 -0400
Subject: [PATCH] Fix some broken tests for Python 3
Remove python2 dependencies in .travis.yml and add python3-paste.
Convert t_daemon.py and jsonwalker.py to python3. csjon has no
python3 version, so replace it with python's built-in JSON module.
python3-pyrad isn't available for Trusty, so krad and OTP tests are
currently not exercised by Travis.
[ghudson@mit.edu: squashed commits; edited commit message]
ticket: 8710
(cherry picked from commit d1fb3551c0dff5c3e6555b31fcbf04ff04d577fe)
[rharwood@redhat.com: .travis.yml]
---
src/lib/krad/t_daemon.py | 2 +-
src/tests/jsonwalker.py | 16 +++++-----------
2 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/src/lib/krad/t_daemon.py b/src/lib/krad/t_daemon.py
index 7d7a5d0c8..7668cd7f8 100755
--- a/src/lib/krad/t_daemon.py
+++ b/src/lib/krad/t_daemon.py
@@ -23,7 +23,7 @@
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import StringIO
+from io import StringIO
import os
import sys
import signal
diff --git a/src/tests/jsonwalker.py b/src/tests/jsonwalker.py
index 7a0675e08..1880363d2 100644
--- a/src/tests/jsonwalker.py
+++ b/src/tests/jsonwalker.py
@@ -1,10 +1,5 @@
import sys
-try:
- import cjson
-except ImportError:
- print("Warning: skipping audit log verification because the cjson module" \
- " is unavailable")
- sys.exit(0)
+import json
from collections import defaultdict
from optparse import OptionParser
@@ -72,7 +67,7 @@ class Parser(object):
"""
Generator that works through dictionary.
"""
- for a,v in adict.iteritems():
+ for a,v in adict.items():
if isinstance(v,dict):
for (attrpath,u) in self._walk(v):
yield (a+'.'+attrpath,u)
@@ -93,17 +88,16 @@ if __name__ == '__main__':
with open(options.filename, 'r') as f:
content = list()
for l in f:
- content.append(cjson.decode(l.rstrip()))
+ content.append(json.loads(l.rstrip()))
f.close()
else:
- print('Input file in jason format is required')
+ print('Input file in JSON format is required')
exit()
defaults = None
if options.defaults is not None:
with open(options.defaults, 'r') as f:
- defaults = cjson.decode(f.read())
- f.close()
+ defaults = json.load(f)
# run test
p = Parser(defaults)

View File

@ -0,0 +1,205 @@
From efb3acd20cbe6330439635a9f297b9dae8a0a5d3 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 5 Mar 2024 19:53:07 -0500
Subject: [PATCH] Fix two unlikely memory leaks
In gss_krb5int_make_seal_token_v3(), one of the bounds checks (which
could probably never be triggered) leaks plain.data. Fix this leak
and use current practices for cleanup throughout the function.
In xmt_rmtcallres() (unused within the tree and likely elsewhere),
store port_ptr into crp->port_ptr as soon as it is allocated;
otherwise it could leak if the subsequent xdr_u_int32() operation
fails.
(cherry picked from commit c5f9c816107f70139de11b38aa02db2f1774ee0d)
---
src/lib/gssapi/krb5/k5sealv3.c | 56 +++++++++++++++-------------------
src/lib/rpc/pmap_rmt.c | 9 +++---
2 files changed, 29 insertions(+), 36 deletions(-)
diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c
index 3b4f8cb837..e881eee835 100644
--- a/src/lib/gssapi/krb5/k5sealv3.c
+++ b/src/lib/gssapi/krb5/k5sealv3.c
@@ -65,7 +65,7 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
int conf_req_flag, int toktype)
{
size_t bufsize = 16;
- unsigned char *outbuf = 0;
+ unsigned char *outbuf = NULL;
krb5_error_code err;
int key_usage;
unsigned char acceptor_flag;
@@ -75,9 +75,13 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
#endif
size_t ec;
unsigned short tok_id;
- krb5_checksum sum;
+ krb5_checksum sum = { 0 };
krb5_key key;
krb5_cksumtype cksumtype;
+ krb5_data plain = empty_data();
+
+ token->value = NULL;
+ token->length = 0;
acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
key_usage = (toktype == KG_TOK_WRAP_MSG
@@ -107,14 +111,15 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
#endif
if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
- krb5_data plain;
krb5_enc_data cipher;
size_t ec_max;
size_t encrypt_size;
/* 300: Adds some slop. */
- if (SIZE_MAX - 300 < message->length)
- return ENOMEM;
+ if (SIZE_MAX - 300 < message->length) {
+ err = ENOMEM;
+ goto cleanup;
+ }
ec_max = SIZE_MAX - message->length - 300;
if (ec_max > 0xffff)
ec_max = 0xffff;
@@ -126,20 +131,20 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
#endif
err = alloc_data(&plain, message->length + 16 + ec);
if (err)
- return err;
+ goto cleanup;
/* Get size of ciphertext. */
encrypt_size = krb5_encrypt_size(plain.length, key->keyblock.enctype);
if (encrypt_size > SIZE_MAX / 2) {
err = ENOMEM;
- goto error;
+ goto cleanup;
}
bufsize = 16 + encrypt_size;
/* Allocate space for header plus encrypted data. */
outbuf = gssalloc_malloc(bufsize);
if (outbuf == NULL) {
- free(plain.data);
- return ENOMEM;
+ err = ENOMEM;
+ goto cleanup;
}
/* TOK_ID */
@@ -164,11 +169,8 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
cipher.ciphertext.length = bufsize - 16;
cipher.enctype = key->keyblock.enctype;
err = krb5_k_encrypt(context, key, key_usage, 0, &plain, &cipher);
- zap(plain.data, plain.length);
- free(plain.data);
- plain.data = 0;
if (err)
- goto error;
+ goto cleanup;
/* Now that we know we're returning a valid token.... */
ctx->seq_send++;
@@ -181,7 +183,6 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
/* If the rotate fails, don't worry about it. */
#endif
} else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
- krb5_data plain;
size_t cksumsize;
/* Here, message is the application-supplied data; message2 is
@@ -193,21 +194,19 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
wrap_with_checksum:
err = alloc_data(&plain, message->length + 16);
if (err)
- return err;
+ goto cleanup;
err = krb5_c_checksum_length(context, cksumtype, &cksumsize);
if (err)
- goto error;
+ goto cleanup;
assert(cksumsize <= 0xffff);
bufsize = 16 + message2->length + cksumsize;
outbuf = gssalloc_malloc(bufsize);
if (outbuf == NULL) {
- free(plain.data);
- plain.data = 0;
err = ENOMEM;
- goto error;
+ goto cleanup;
}
/* TOK_ID */
@@ -239,23 +238,15 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
if (message2->length)
memcpy(outbuf + 16, message2->value, message2->length);
- sum.contents = outbuf + 16 + message2->length;
- sum.length = cksumsize;
-
err = krb5_k_make_checksum(context, cksumtype, key,
key_usage, &plain, &sum);
- zap(plain.data, plain.length);
- free(plain.data);
- plain.data = 0;
if (err) {
zap(outbuf,bufsize);
- goto error;
+ goto cleanup;
}
if (sum.length != cksumsize)
abort();
memcpy(outbuf + 16 + message2->length, sum.contents, cksumsize);
- krb5_free_checksum_contents(context, &sum);
- sum.contents = 0;
/* Now that we know we're actually generating the token... */
ctx->seq_send++;
@@ -285,12 +276,13 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
token->value = outbuf;
token->length = bufsize;
- return 0;
+ outbuf = NULL;
+ err = 0;
-error:
+cleanup:
+ krb5_free_checksum_contents(context, &sum);
+ zapfree(plain.data, plain.length);
gssalloc_free(outbuf);
- token->value = NULL;
- token->length = 0;
return err;
}
diff --git a/src/lib/rpc/pmap_rmt.c b/src/lib/rpc/pmap_rmt.c
index 8c7e30c21a..0748af34a7 100644
--- a/src/lib/rpc/pmap_rmt.c
+++ b/src/lib/rpc/pmap_rmt.c
@@ -160,11 +160,12 @@ xdr_rmtcallres(
caddr_t port_ptr;
port_ptr = (caddr_t)(void *)crp->port_ptr;
- if (xdr_reference(xdrs, &port_ptr, sizeof (uint32_t),
- xdr_u_int32) && xdr_u_int32(xdrs, &crp->resultslen)) {
- crp->port_ptr = (uint32_t *)(void *)port_ptr;
+ if (!xdr_reference(xdrs, &port_ptr, sizeof (uint32_t),
+ (xdrproc_t)xdr_u_int32))
+ return (FALSE);
+ crp->port_ptr = (uint32_t *)(void *)port_ptr;
+ if (xdr_u_int32(xdrs, &crp->resultslen))
return ((*(crp->xdr_results))(xdrs, crp->results_ptr));
- }
return (FALSE);
}
--
2.44.0

View File

@ -0,0 +1,38 @@
From 5a0833a3f3b1c44edd08425d98f682b96ad7a01e Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 14 May 2020 15:01:18 -0400
Subject: [PATCH] Fix typo ("in in") in the ksu man page
(cherry picked from commit 1011841acdc1020f308ef4f569c6622f279d8c3f)
(cherry picked from commit 8de669742ae4190542741f0dc61119a6a0dad666)
---
doc/user/user_commands/ksu.rst | 2 +-
src/man/ksu.man | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/user/user_commands/ksu.rst b/doc/user/user_commands/ksu.rst
index 8d6c7ef79..933738229 100644
--- a/doc/user/user_commands/ksu.rst
+++ b/doc/user/user_commands/ksu.rst
@@ -155,7 +155,7 @@ wrong password is typed in, ksu fails.
.. note::
During authentication, only the tickets that could be
- obtained without providing a password are cached in in the
+ obtained without providing a password are cached in the
source cache.
diff --git a/src/man/ksu.man b/src/man/ksu.man
index 81e34815d..8d4c6a359 100644
--- a/src/man/ksu.man
+++ b/src/man/ksu.man
@@ -176,7 +176,7 @@ wrong password is typed in, ksu fails.
.INDENT 0.0
.INDENT 3.5
During authentication, only the tickets that could be
-obtained without providing a password are cached in in the
+obtained without providing a password are cached in the
source cache.
.UNINDENT
.UNINDENT

View File

@ -0,0 +1,535 @@
From c73e8ed9f89fdc709d15656b6431492d43de94ce Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 14 Jun 2024 10:56:12 -0400
Subject: [PATCH] Fix vulnerabilities in GSS message token handling
In gss_krb5int_unseal_token_v3() and gss_krb5int_unseal_v3_iov(),
verify the Extra Count field of CFX wrap tokens against the encrypted
header. Reported by Jacob Champion.
In gss_krb5int_unseal_token_v3(), check for a decrypted plaintext
length too short to contain the encrypted header and extra count
bytes. Reported by Jacob Champion.
In kg_unseal_iov_token(), separately track the header IOV length and
complete token length when parsing the token's ASN.1 wrapper. This
fix contains modified versions of functions from k5-der.h and
util_token.c; this duplication will be cleaned up in a future commit.
CVE-2024-37370:
In MIT krb5 release 1.3 and later, an attacker can modify the
plaintext Extra Count field of a confidential GSS krb5 wrap token,
causing the unwrapped token to appear truncated to the application.
CVE-2024-37371:
In MIT krb5 release 1.3 and later, an attacker can cause invalid
memory reads by sending message tokens with invalid length fields.
ticket: 9128 (new)
tags: pullup
target_version: 1.21-next
(cherry picked from commit b0a2f8a5365f2eec3e27d78907de9f9d2c80505a)
---
src/lib/gssapi/krb5/k5sealv3.c | 5 +
src/lib/gssapi/krb5/k5sealv3iov.c | 3 +-
src/lib/gssapi/krb5/k5unsealiov.c | 80 +++++++++-
src/tests/gssapi/t_invalid.c | 233 +++++++++++++++++++++++++-----
4 files changed, 275 insertions(+), 46 deletions(-)
diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c
index e881eee835..d3210c1107 100644
--- a/src/lib/gssapi/krb5/k5sealv3.c
+++ b/src/lib/gssapi/krb5/k5sealv3.c
@@ -400,10 +400,15 @@ gss_krb5int_unseal_token_v3(krb5_context *contextptr,
/* Don't use bodysize here! Use the fact that
cipher.ciphertext.length has been adjusted to the
correct length. */
+ if (plain.length < 16 + ec) {
+ free(plain.data);
+ goto defective;
+ }
althdr = (unsigned char *)plain.data + plain.length - 16;
if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
|| althdr[2] != ptr[2]
|| althdr[3] != ptr[3]
+ || load_16_be(althdr+4) != ec
|| memcmp(althdr+8, ptr+8, 8)) {
free(plain.data);
goto defective;
diff --git a/src/lib/gssapi/krb5/k5sealv3iov.c b/src/lib/gssapi/krb5/k5sealv3iov.c
index 333ee124dd..f8e90c35b4 100644
--- a/src/lib/gssapi/krb5/k5sealv3iov.c
+++ b/src/lib/gssapi/krb5/k5sealv3iov.c
@@ -402,9 +402,10 @@ gss_krb5int_unseal_v3_iov(krb5_context context,
if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
|| althdr[2] != ptr[2]
|| althdr[3] != ptr[3]
+ || load_16_be(althdr + 4) != ec
|| memcmp(althdr + 8, ptr + 8, 8) != 0) {
*minor_status = 0;
- return GSS_S_BAD_SIG;
+ return GSS_S_DEFECTIVE_TOKEN;
}
} else {
/* Verify checksum: note EC is checksum size here, not padding */
diff --git a/src/lib/gssapi/krb5/k5unsealiov.c b/src/lib/gssapi/krb5/k5unsealiov.c
index 3ce2a90ce9..6a6585d9af 100644
--- a/src/lib/gssapi/krb5/k5unsealiov.c
+++ b/src/lib/gssapi/krb5/k5unsealiov.c
@@ -25,6 +25,7 @@
*/
#include "k5-int.h"
+#include "k5-der.h"
#include "gssapiP_krb5.h"
static OM_uint32
@@ -247,6 +248,73 @@ cleanup:
return retval;
}
+/* Similar to k5_der_get_value(), but output an unchecked content length
+ * instead of a k5input containing the contents. */
+static inline bool
+get_der_tag(struct k5input *in, uint8_t idbyte, size_t *len_out)
+{
+ uint8_t lenbyte, i;
+ size_t len;
+
+ /* Do nothing if in is empty or the next byte doesn't match idbyte. */
+ if (in->status || in->len == 0 || *in->ptr != idbyte)
+ return false;
+
+ /* Advance past the identifier byte and decode the length. */
+ (void)k5_input_get_byte(in);
+ lenbyte = k5_input_get_byte(in);
+ if (lenbyte < 128) {
+ len = lenbyte;
+ } else {
+ len = 0;
+ for (i = 0; i < (lenbyte & 0x7F); i++) {
+ if (len > (SIZE_MAX >> 8)) {
+ k5_input_set_status(in, EOVERFLOW);
+ return false;
+ }
+ len = (len << 8) | k5_input_get_byte(in);
+ }
+ }
+
+ if (in->status)
+ return false;
+
+ *len_out = len;
+ return true;
+}
+
+/*
+ * Similar to g_verify_token_header() without toktype or flags, but do not read
+ * more than *header_len bytes of ASN.1 wrapper, and on output set *header_len
+ * to the remaining number of header bytes. Verify the outer DER tag's length
+ * against token_len, which may be larger (but not smaller) than *header_len.
+ */
+static gss_int32
+verify_detached_wrapper(const gss_OID_desc *mech, size_t *header_len,
+ uint8_t **header_in, size_t token_len)
+{
+ struct k5input in, mech_der;
+ gss_OID_desc toid;
+ size_t len;
+
+ k5_input_init(&in, *header_in, *header_len);
+
+ if (get_der_tag(&in, 0x60, &len)) {
+ if (len != token_len - (in.ptr - *header_in))
+ return G_BAD_TOK_HEADER;
+ if (!k5_der_get_value(&in, 0x06, &mech_der))
+ return G_BAD_TOK_HEADER;
+ toid.elements = (uint8_t *)mech_der.ptr;
+ toid.length = mech_der.len;
+ if (!g_OID_equal(&toid, mech))
+ return G_WRONG_MECH;
+ }
+
+ *header_in = (uint8_t *)in.ptr;
+ *header_len = in.len;
+ return 0;
+}
+
/*
* Caller must provide TOKEN | DATA | PADDING | TRAILER, except
* for DCE in which case it can just provide TOKEN | DATA (must
@@ -267,8 +335,7 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
gss_iov_buffer_t header;
gss_iov_buffer_t padding;
gss_iov_buffer_t trailer;
- size_t input_length;
- unsigned int bodysize;
+ size_t input_length, hlen;
int toktype2;
header = kg_locate_header_iov(iov, iov_count, toktype);
@@ -298,15 +365,14 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
input_length += trailer->buffer.length;
}
- code = g_verify_token_header(ctx->mech_used,
- &bodysize, &ptr, -1,
- input_length, 0);
+ hlen = header->buffer.length;
+ code = verify_detached_wrapper(ctx->mech_used, &hlen, &ptr, input_length);
if (code != 0) {
*minor_status = code;
return GSS_S_DEFECTIVE_TOKEN;
}
- if (bodysize < 2) {
+ if (hlen < 2) {
*minor_status = (OM_uint32)G_BAD_TOK_HEADER;
return GSS_S_DEFECTIVE_TOKEN;
}
@@ -314,7 +380,7 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
toktype2 = load_16_be(ptr);
ptr += 2;
- bodysize -= 2;
+ hlen -= 2;
switch (toktype2) {
case KG2_TOK_MIC_MSG:
diff --git a/src/tests/gssapi/t_invalid.c b/src/tests/gssapi/t_invalid.c
index fb8fe55111..8192935099 100644
--- a/src/tests/gssapi/t_invalid.c
+++ b/src/tests/gssapi/t_invalid.c
@@ -36,31 +36,41 @@
*
* 1. A pre-CFX wrap or MIC token processed with a CFX-only context causes a
* null pointer dereference. (The token must use SEAL_ALG_NONE or it will
- * be rejected.)
+ * be rejected.) This vulnerability also applies to IOV unwrap.
*
- * 2. A pre-CFX wrap or MIC token with fewer than 24 bytes after the ASN.1
+ * 2. A CFX wrap token with a different value of EC between the plaintext and
+ * encrypted copies will be erroneously accepted, which allows a message
+ * truncation attack. This vulnerability also applies to IOV unwrap.
+ *
+ * 3. A CFX wrap token with a plaintext length fewer than 16 bytes causes an
+ * access before the beginning of the input buffer, possibly leading to a
+ * crash.
+ *
+ * 4. A CFX wrap token with a plaintext EC value greater than the plaintext
+ * length - 16 causes an integer underflow when computing the result length,
+ * likely causing a crash.
+ *
+ * 5. An IOV unwrap operation will overrun the header buffer if an ASN.1
+ * wrapper longer than the header buffer is present.
+ *
+ * 6. A pre-CFX wrap or MIC token with fewer than 24 bytes after the ASN.1
* header causes an input buffer overrun, usually leading to either a segv
* or a GSS_S_DEFECTIVE_TOKEN error due to garbage algorithm, filler, or
- * sequence number values.
+ * sequence number values. This vulnerability also applies to IOV unwrap.
*
- * 3. A pre-CFX wrap token with fewer than 16 + cksumlen bytes after the ASN.1
+ * 7. A pre-CFX wrap token with fewer than 16 + cksumlen bytes after the ASN.1
* header causes an integer underflow when computing the ciphertext length,
* leading to an allocation error on 32-bit platforms or a segv on 64-bit
* platforms. A pre-CFX MIC token of this size causes an input buffer
* overrun when comparing the checksum, perhaps leading to a segv.
*
- * 4. A pre-CFX wrap token with fewer than conflen + padlen bytes in the
+ * 8. A pre-CFX wrap token with fewer than conflen + padlen bytes in the
* ciphertext (where padlen is the last byte of the decrypted ciphertext)
* causes an integer underflow when computing the original message length,
* leading to an allocation error.
*
- * 5. In the mechglue, truncated encapsulation in the initial context token can
+ * 9. In the mechglue, truncated encapsulation in the initial context token can
* cause input buffer overruns in gss_accept_sec_context().
- *
- * Vulnerabilities #1 and #2 also apply to IOV unwrap, although tokens with
- * fewer than 16 bytes after the ASN.1 header will be rejected.
- * Vulnerabilities #2 and #5 can only be robustly detected using a
- * memory-checking environment such as valgrind.
*/
#include "k5-int.h"
@@ -97,17 +107,25 @@ struct test {
}
};
-/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key. */
+static void *
+ealloc(size_t len)
+{
+ void *ptr = calloc(len, 1);
+
+ if (ptr == NULL)
+ abort();
+ return ptr;
+}
+
+/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key.
+ * The context takes ownership of subkey. */
static gss_ctx_id_t
-make_fake_cfx_context()
+make_fake_cfx_context(krb5_key subkey)
{
gss_union_ctx_id_t uctx;
krb5_gss_ctx_id_t kgctx;
- krb5_keyblock kb;
- kgctx = calloc(1, sizeof(*kgctx));
- if (kgctx == NULL)
- abort();
+ kgctx = ealloc(sizeof(*kgctx));
kgctx->established = 1;
kgctx->proto = 1;
if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0)
@@ -116,15 +134,10 @@ make_fake_cfx_context()
kgctx->sealalg = -1;
kgctx->signalg = -1;
- kb.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
- kb.length = 16;
- kb.contents = (unsigned char *)"1234567887654321";
- if (krb5_k_create_key(NULL, &kb, &kgctx->subkey) != 0)
- abort();
+ kgctx->subkey = subkey;
+ kgctx->cksumtype = CKSUMTYPE_HMAC_SHA1_96_AES128;
- uctx = calloc(1, sizeof(*uctx));
- if (uctx == NULL)
- abort();
+ uctx = ealloc(sizeof(*uctx));
uctx->mech_type = &mech_krb5;
uctx->internal_ctx_id = (gss_ctx_id_t)kgctx;
return (gss_ctx_id_t)uctx;
@@ -138,9 +151,7 @@ make_fake_context(const struct test *test)
krb5_gss_ctx_id_t kgctx;
krb5_keyblock kb;
- kgctx = calloc(1, sizeof(*kgctx));
- if (kgctx == NULL)
- abort();
+ kgctx = ealloc(sizeof(*kgctx));
kgctx->established = 1;
if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0)
abort();
@@ -162,9 +173,7 @@ make_fake_context(const struct test *test)
if (krb5_k_create_key(NULL, &kb, &kgctx->enc) != 0)
abort();
- uctx = calloc(1, sizeof(*uctx));
- if (uctx == NULL)
- abort();
+ uctx = ealloc(sizeof(*uctx));
uctx->mech_type = &mech_krb5;
uctx->internal_ctx_id = (gss_ctx_id_t)kgctx;
return (gss_ctx_id_t)uctx;
@@ -194,9 +203,7 @@ make_token(unsigned char *token, size_t len, gss_buffer_t out)
assert(mech_krb5.length == 9);
assert(len + 11 < 128);
- wrapped = malloc(len + 13);
- if (wrapped == NULL)
- abort();
+ wrapped = ealloc(len + 13);
wrapped[0] = 0x60;
wrapped[1] = len + 11;
wrapped[2] = 0x06;
@@ -207,6 +214,18 @@ make_token(unsigned char *token, size_t len, gss_buffer_t out)
out->value = wrapped;
}
+/* Create a 16-byte header for a CFX confidential wrap token to be processed by
+ * the fake CFX context. */
+static void
+write_cfx_header(uint16_t ec, uint8_t *out)
+{
+ memset(out, 0, 16);
+ store_16_be(KG2_TOK_WRAP_MSG, out);
+ out[2] = FLAG_WRAP_CONFIDENTIAL;
+ out[3] = 0xFF;
+ store_16_be(ec, out + 4);
+}
+
/* Unwrap a superficially valid RFC 1964 token with a CFX-only context, with
* regular and IOV unwrap. */
static void
@@ -238,6 +257,134 @@ test_bogus_1964_token(gss_ctx_id_t ctx)
free(in.value);
}
+static void
+test_cfx_altered_ec(gss_ctx_id_t ctx, krb5_key subkey)
+{
+ OM_uint32 major, minor;
+ uint8_t tokbuf[128], plainbuf[24];
+ krb5_data plain;
+ krb5_enc_data cipher;
+ gss_buffer_desc in, out;
+ gss_iov_buffer_desc iov[2];
+
+ /* Construct a header with a plaintext EC value of 3. */
+ write_cfx_header(3, tokbuf);
+
+ /* Encrypt a plaintext and a copy of the header with the EC value 0. */
+ memcpy(plainbuf, "truncate", 8);
+ memcpy(plainbuf + 8, tokbuf, 16);
+ store_16_be(0, plainbuf + 12);
+ plain = make_data(plainbuf, 24);
+ cipher.ciphertext.data = (char *)tokbuf + 16;
+ cipher.ciphertext.length = sizeof(tokbuf) - 16;
+ cipher.enctype = subkey->keyblock.enctype;
+ if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL,
+ &plain, &cipher) != 0)
+ abort();
+
+ /* Verify that the token is rejected by gss_unwrap(). */
+ in.value = tokbuf;
+ in.length = 16 + cipher.ciphertext.length;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+
+ /* Verify that the token is rejected by gss_unwrap_iov(). */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
+ iov[0].buffer = in;
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 2);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+}
+
+static void
+test_cfx_short_plaintext(gss_ctx_id_t ctx, krb5_key subkey)
+{
+ OM_uint32 major, minor;
+ uint8_t tokbuf[128], zerobyte = 0;
+ krb5_data plain;
+ krb5_enc_data cipher;
+ gss_buffer_desc in, out;
+
+ write_cfx_header(0, tokbuf);
+
+ /* Encrypt a single byte, with no copy of the header. */
+ plain = make_data(&zerobyte, 1);
+ cipher.ciphertext.data = (char *)tokbuf + 16;
+ cipher.ciphertext.length = sizeof(tokbuf) - 16;
+ cipher.enctype = subkey->keyblock.enctype;
+ if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL,
+ &plain, &cipher) != 0)
+ abort();
+
+ /* Verify that the token is rejected by gss_unwrap(). */
+ in.value = tokbuf;
+ in.length = 16 + cipher.ciphertext.length;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+}
+
+static void
+test_cfx_large_ec(gss_ctx_id_t ctx, krb5_key subkey)
+{
+ OM_uint32 major, minor;
+ uint8_t tokbuf[128] = { 0 }, plainbuf[20];
+ krb5_data plain;
+ krb5_enc_data cipher;
+ gss_buffer_desc in, out;
+
+ /* Construct a header with an EC value of 5. */
+ write_cfx_header(5, tokbuf);
+
+ /* Encrypt a 4-byte plaintext plus the header. */
+ memcpy(plainbuf, "abcd", 4);
+ memcpy(plainbuf + 4, tokbuf, 16);
+ plain = make_data(plainbuf, 20);
+ cipher.ciphertext.data = (char *)tokbuf + 16;
+ cipher.ciphertext.length = sizeof(tokbuf) - 16;
+ cipher.enctype = subkey->keyblock.enctype;
+ if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL,
+ &plain, &cipher) != 0)
+ abort();
+
+ /* Verify that the token is rejected by gss_unwrap(). */
+ in.value = tokbuf;
+ in.length = 16 + cipher.ciphertext.length;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+}
+
+static void
+test_iov_large_asn1_wrapper(gss_ctx_id_t ctx)
+{
+ OM_uint32 minor, major;
+ uint8_t databuf[10] = { 0 };
+ gss_iov_buffer_desc iov[2];
+
+ /*
+ * In this IOV array, the header contains a DER tag with a dangling eight
+ * bytes of length field. The data IOV indicates a total token length
+ * sufficient to contain the length bytes.
+ */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov[0].buffer.value = ealloc(2);
+ iov[0].buffer.length = 2;
+ memcpy(iov[0].buffer.value, "\x60\x88", 2);
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[1].buffer.value = databuf;
+ iov[1].buffer.length = 10;
+ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 2);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(iov[0].buffer.value);
+}
+
/* Process wrap and MIC tokens with incomplete headers. */
static void
test_short_header(gss_ctx_id_t ctx)
@@ -387,9 +534,7 @@ try_accept(void *value, size_t len)
gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
/* Copy the provided value to make input overruns more obvious. */
- in.value = malloc(len);
- if (in.value == NULL)
- abort();
+ in.value = ealloc(len);
memcpy(in.value, value, len);
in.length = len;
(void)gss_accept_sec_context(&minor, &ctx, GSS_C_NO_CREDENTIAL, &in,
@@ -424,11 +569,23 @@ test_short_encapsulation()
int
main(int argc, char **argv)
{
+ krb5_keyblock kb;
+ krb5_key cfx_subkey;
gss_ctx_id_t ctx;
size_t i;
- ctx = make_fake_cfx_context();
+ kb.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
+ kb.length = 16;
+ kb.contents = (unsigned char *)"1234567887654321";
+ if (krb5_k_create_key(NULL, &kb, &cfx_subkey) != 0)
+ abort();
+
+ ctx = make_fake_cfx_context(cfx_subkey);
test_bogus_1964_token(ctx);
+ test_cfx_altered_ec(ctx, cfx_subkey);
+ test_cfx_short_plaintext(ctx, cfx_subkey);
+ test_cfx_large_ec(ctx, cfx_subkey);
+ test_iov_large_asn1_wrapper(ctx);
free_fake_context(ctx);
for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
--
2.45.1

View File

@ -0,0 +1,629 @@
From aad00d346e5c7923287fc0016a37b49c4618d78e Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Thu, 22 Aug 2024 17:15:50 +0200
Subject: [PATCH] Generate and verify message MACs in libkrad
Implement some of the measures specified in
draft-ietf-radext-deprecating-radius-03 for mitigating the BlastRADIUS
attack (CVE-2024-3596):
* Include a Message-Authenticator MAC as the first attribute when
generating a packet of type Access-Request, Access-Reject,
Access-Accept, or Access-Challenge (sections 5.2.1 and 5.2.4), if
the secret is non-empty. (An empty secret indicates the use of Unix
domain socket transport.)
* Validate the Message-Authenticator MAC in received packets, if
present.
FreeRADIUS enforces Message-Authenticator as of versions 3.2.5 and
3.0.27. libkrad must generate Message-Authenticator attributes in
order to remain compatible with these implementations.
[ghudson@mit.edu: adjusted style and naming; simplified some
functions; edited commit message]
ticket: 9142 (new)
tags: pullup
target_version: 1.21-next
(cherry picked from commit 871125fea8ce0370a972bf65f7d1de63f619b06c)
---
src/include/k5-int.h | 5 +
src/lib/crypto/krb/checksum_hmac_md5.c | 28 ++++
src/lib/crypto/libk5crypto.exports | 1 +
src/lib/krad/attr.c | 17 ++
src/lib/krad/attrset.c | 59 +++++--
src/lib/krad/internal.h | 7 +-
src/lib/krad/packet.c | 206 +++++++++++++++++++++++--
src/lib/krad/t_attrset.c | 2 +-
src/lib/krad/t_daemon.py | 3 +-
src/lib/krad/t_packet.c | 11 ++
src/tests/t_otp.py | 3 +
11 files changed, 311 insertions(+), 31 deletions(-)
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 9d5e41ca2c..d062617268 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -2415,4 +2415,9 @@ void k5_change_error_message_code(krb5_context ctx, krb5_error_code oldcode,
#define k5_prependmsg krb5_prepend_error_message
#define k5_wrapmsg krb5_wrap_error_message
+/* Generate an HMAC-MD5 keyed checksum as specified by RFC 2104. */
+krb5_error_code
+k5_hmac_md5(const krb5_data *key, const krb5_crypto_iov *data, size_t num_data,
+ krb5_data *output);
+
#endif /* _KRB5_INT_H */
diff --git a/src/lib/crypto/krb/checksum_hmac_md5.c b/src/lib/crypto/krb/checksum_hmac_md5.c
index ec024f3966..a809388549 100644
--- a/src/lib/crypto/krb/checksum_hmac_md5.c
+++ b/src/lib/crypto/krb/checksum_hmac_md5.c
@@ -92,3 +92,31 @@ cleanup:
free(hash_iov);
return ret;
}
+
+krb5_error_code
+k5_hmac_md5(const krb5_data *key, const krb5_crypto_iov *data, size_t num_data,
+ krb5_data *output)
+{
+ krb5_error_code ret;
+ const struct krb5_hash_provider *hash = &krb5int_hash_md5;
+ krb5_keyblock keyblock = { 0 };
+ krb5_data hashed_key;
+ uint8_t hkeybuf[16];
+ krb5_crypto_iov iov;
+
+ /* Hash the key if it is longer than the block size. */
+ if (key->length > hash->blocksize) {
+ hashed_key = make_data(hkeybuf, sizeof(hkeybuf));
+ iov.flags = KRB5_CRYPTO_TYPE_DATA;
+ iov.data = *key;
+ ret = hash->hash(&iov, 1, &hashed_key);
+ if (ret)
+ return ret;
+ key = &hashed_key;
+ }
+
+ keyblock.magic = KV5M_KEYBLOCK;
+ keyblock.length = key->length;
+ keyblock.contents = (uint8_t *)key->data;
+ return krb5int_hmac_keyblock(hash, &keyblock, data, num_data, output);
+}
diff --git a/src/lib/crypto/libk5crypto.exports b/src/lib/crypto/libk5crypto.exports
index 9db1813810..b4dcd29937 100644
--- a/src/lib/crypto/libk5crypto.exports
+++ b/src/lib/crypto/libk5crypto.exports
@@ -107,3 +107,4 @@ krb5_c_prfplus
krb5_c_derive_prfplus
k5_enctype_to_ssf
krb5int_c_deprecated_enctype
+k5_hmac_md5
diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
index 42d354a3b5..65ed1d35e7 100644
--- a/src/lib/krad/attr.c
+++ b/src/lib/krad/attr.c
@@ -125,6 +125,23 @@ static const attribute_record attributes[UCHAR_MAX] = {
{"NAS-Port-Type", 4, 4, NULL, NULL},
{"Port-Limit", 4, 4, NULL, NULL},
{"Login-LAT-Port", 1, MAX_ATTRSIZE, NULL, NULL},
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Password-Retry */
+ {NULL, 0, 0, NULL, NULL}, /* Prompt */
+ {NULL, 0, 0, NULL, NULL}, /* Connect-Info */
+ {NULL, 0, 0, NULL, NULL}, /* Configuration-Token */
+ {NULL, 0, 0, NULL, NULL}, /* EAP-Message */
+ {"Message-Authenticator", MD5_DIGEST_SIZE, MD5_DIGEST_SIZE, NULL, NULL},
};
/* Encode User-Password attribute. */
diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c
index 6ec031e320..e5457ebfd7 100644
--- a/src/lib/krad/attrset.c
+++ b/src/lib/krad/attrset.c
@@ -164,15 +164,44 @@ krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
return 0;
}
+/* Place an encoded attributes into outbuf at position *i. Increment *i by the
+ * length of the encoding. */
+static krb5_error_code
+append_attr(krb5_context ctx, const char *secret,
+ const uint8_t *auth, krad_attr type, const krb5_data *data,
+ uint8_t outbuf[MAX_ATTRSETSIZE], size_t *i, krb5_boolean *is_fips)
+{
+ uint8_t buffer[MAX_ATTRSIZE];
+ size_t attrlen;
+ krb5_error_code retval;
+
+ retval = kr_attr_encode(ctx, secret, auth, type, data, buffer, &attrlen,
+ is_fips);
+ if (retval)
+ return retval;
+
+ if (attrlen > MAX_ATTRSETSIZE - *i - 2)
+ return EMSGSIZE;
+
+ outbuf[(*i)++] = type;
+ outbuf[(*i)++] = attrlen + 2;
+ memcpy(outbuf + *i, buffer, attrlen);
+ *i += attrlen;
+
+ return 0;
+}
+
krb5_error_code
kr_attrset_encode(const krad_attrset *set, const char *secret,
- const unsigned char *auth,
+ const uint8_t *auth, krb5_boolean add_msgauth,
unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
krb5_boolean *is_fips)
{
- unsigned char buffer[MAX_ATTRSIZE];
krb5_error_code retval;
- size_t i = 0, attrlen;
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+ const uint8_t zeroes[MD5_DIGEST_SIZE] = { 0 };
+ krb5_data zerodata;
+ size_t i = 0;
attr *a;
if (set == NULL) {
@@ -180,19 +209,21 @@ kr_attrset_encode(const krad_attrset *set, const char *secret,
return 0;
}
- K5_TAILQ_FOREACH(a, &set->list, list) {
- retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
- buffer, &attrlen, is_fips);
- if (retval != 0)
+ if (add_msgauth) {
+ /* Encode Message-Authenticator as the first attribute, per
+ * draft-ietf-radext-deprecating-radius-03 section 5.2. */
+ zerodata = make_data((uint8_t *)zeroes, MD5_DIGEST_SIZE);
+ retval = append_attr(set->ctx, secret, auth, msgauth_type, &zerodata,
+ outbuf, &i, is_fips);
+ if (retval)
return retval;
+ }
- if (i + attrlen + 2 > MAX_ATTRSETSIZE)
- return EMSGSIZE;
-
- outbuf[i++] = a->type;
- outbuf[i++] = attrlen + 2;
- memcpy(&outbuf[i], buffer, attrlen);
- i += attrlen;
+ K5_TAILQ_FOREACH(a, &set->list, list) {
+ retval = append_attr(set->ctx, secret, auth, a->type, &a->attr,
+ outbuf, &i, is_fips);
+ if (retval)
+ return retval;
}
*outlen = i;
diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
index b086598fb2..f3e4a1d8d3 100644
--- a/src/lib/krad/internal.h
+++ b/src/lib/krad/internal.h
@@ -45,6 +45,8 @@
#define UCHAR_MAX 255
#endif
+#define MD5_DIGEST_SIZE 16
+
/* RFC 2865 */
#define MAX_ATTRSIZE (UCHAR_MAX - 2)
#define MAX_ATTRSETSIZE (KRAD_PACKET_SIZE_MAX - 20)
@@ -75,10 +77,11 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
krad_attr type, const krb5_data *in,
unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-/* Encode the attributes into the buffer. */
+/* Encode set into outbuf. If add_msgauth is true, include a zeroed
+ * Message-Authenticator as the first attribute. */
krb5_error_code
kr_attrset_encode(const krad_attrset *set, const char *secret,
- const unsigned char *auth,
+ const uint8_t *auth, krb5_boolean add_msgauth,
unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
krb5_boolean *is_fips);
diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
index fc2d248001..257bbc6345 100644
--- a/src/lib/krad/packet.c
+++ b/src/lib/krad/packet.c
@@ -36,6 +36,7 @@
typedef unsigned char uchar;
/* RFC 2865 */
+#define MSGAUTH_SIZE (2 + MD5_DIGEST_SIZE)
#define OFFSET_CODE 0
#define OFFSET_ID 1
#define OFFSET_LENGTH 2
@@ -222,6 +223,106 @@ packet_set_attrset(krb5_context ctx, const char *secret, krad_packet *pkt)
return kr_attrset_decode(ctx, &tmp, secret, pkt_auth(pkt), &pkt->attrset);
}
+/* Determine if a packet requires a Message-Authenticator attribute. */
+static inline krb5_boolean
+requires_msgauth(const char *secret, krad_code code)
+{
+ /* If no secret is provided, assume that the transport is a UNIX socket.
+ * Message-Authenticator is required only on UDP and TCP connections. */
+ if (*secret == '\0')
+ return FALSE;
+
+ /*
+ * Per draft-ietf-radext-deprecating-radius-03 sections 5.2.1 and 5.2.4,
+ * Message-Authenticator is required in Access-Request packets and all
+ * potential responses when UDP or TCP transport is used.
+ */
+ return code == krad_code_name2num("Access-Request") ||
+ code == krad_code_name2num("Access-Reject") ||
+ code == krad_code_name2num("Access-Accept") ||
+ code == krad_code_name2num("Access-Challenge");
+}
+
+/* Check if the packet has a Message-Authenticator attribute. */
+static inline krb5_boolean
+has_pkt_msgauth(const krad_packet *pkt)
+{
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+
+ return krad_attrset_get(pkt->attrset, msgauth_type, 0) != NULL;
+}
+
+/* Return the beginning of the Message-Authenticator attribute in pkt, or NULL
+ * if no such attribute is present. */
+static const uint8_t *
+lookup_msgauth_addr(const krad_packet *pkt)
+{
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+ size_t i;
+ uint8_t *p;
+
+ i = OFFSET_ATTR;
+ while (i + 2 < pkt->pkt.length) {
+ p = (uint8_t *)offset(&pkt->pkt, i);
+ if (msgauth_type == *p)
+ return p;
+ i += p[1];
+ }
+
+ return NULL;
+}
+
+/*
+ * Calculate the message authenticator MAC for pkt as specified in RFC 2869
+ * section 5.14, placing the result in mac_out. Use the provided authenticator
+ * auth, which may be from pkt or from a corresponding request.
+ */
+static krb5_error_code
+calculate_mac(const char *secret, const krad_packet *pkt,
+ const uint8_t auth[AUTH_FIELD_SIZE],
+ uint8_t mac_out[MD5_DIGEST_SIZE])
+{
+ uint8_t zeroed_msgauth[MSGAUTH_SIZE];
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+ const uint8_t *msgauth_attr, *msgauth_end, *pkt_end;
+ krb5_crypto_iov input[5];
+ krb5_data ksecr, mac;
+
+ msgauth_attr = lookup_msgauth_addr(pkt);
+ if (msgauth_attr == NULL)
+ return EINVAL;
+ msgauth_end = msgauth_attr + MSGAUTH_SIZE;
+ pkt_end = (const uint8_t *)pkt->pkt.data + pkt->pkt.length;
+
+ /* Read code, id, and length from the packet. */
+ input[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[0].data = make_data(pkt->pkt.data, OFFSET_AUTH);
+
+ /* Read the provided authenticator. */
+ input[1].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[1].data = make_data((uint8_t *)auth, AUTH_FIELD_SIZE);
+
+ /* Read any attributes before Message-Authenticator. */
+ input[2].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[2].data = make_data(pkt_attr(pkt), msgauth_attr - pkt_attr(pkt));
+
+ /* Read Message-Authenticator with the data bytes all set to zero, per RFC
+ * 2869 section 5.14. */
+ zeroed_msgauth[0] = msgauth_type;
+ zeroed_msgauth[1] = MSGAUTH_SIZE;
+ memset(zeroed_msgauth + 2, 0, MD5_DIGEST_SIZE);
+ input[3].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[3].data = make_data(zeroed_msgauth, MSGAUTH_SIZE);
+
+ /* Read any attributes after Message-Authenticator. */
+ input[4].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[4].data = make_data((uint8_t *)msgauth_end, pkt_end - msgauth_end);
+
+ mac = make_data(mac_out, MD5_DIGEST_SIZE);
+ ksecr = string2data((char *)secret);
+ return k5_hmac_md5(&ksecr, input, 5, &mac);
+}
+
ssize_t
krad_packet_bytes_needed(const krb5_data *buffer)
{
@@ -255,6 +356,7 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
krad_packet *pkt;
uchar id;
size_t attrset_len;
+ krb5_boolean msgauth_required;
pkt = packet_new();
if (pkt == NULL) {
@@ -274,9 +376,13 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
if (retval != 0)
goto error;
+ /* Determine if Message-Authenticator is required. */
+ msgauth_required = (*secret != '\0' &&
+ code == krad_code_name2num("Access-Request"));
+
/* Encode the attributes. */
- retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt),
- &attrset_len, &pkt->is_fips);
+ retval = kr_attrset_encode(set, secret, pkt_auth(pkt), msgauth_required,
+ pkt_attr(pkt), &attrset_len, &pkt->is_fips);
if (retval != 0)
goto error;
@@ -285,6 +391,13 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
pkt_code_set(pkt, code);
pkt_len_set(pkt, pkt->pkt.length);
+ if (msgauth_required) {
+ /* Calculate and set the Message-Authenticator MAC. */
+ retval = calculate_mac(secret, pkt, pkt_auth(pkt), pkt_attr(pkt) + 2);
+ if (retval != 0)
+ goto error;
+ }
+
/* Copy the attrset for future use. */
retval = packet_set_attrset(ctx, secret, pkt);
if (retval != 0)
@@ -307,14 +420,19 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
krb5_error_code retval;
krad_packet *pkt;
size_t attrset_len;
+ krb5_boolean msgauth_required;
pkt = packet_new();
if (pkt == NULL)
return ENOMEM;
+ /* Determine if Message-Authenticator is required. */
+ msgauth_required = requires_msgauth(secret, code);
+
/* Encode the attributes. */
- retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt),
- &attrset_len, &pkt->is_fips);
+ retval = kr_attrset_encode(set, secret, pkt_auth(request),
+ msgauth_required, pkt_attr(pkt), &attrset_len,
+ &pkt->is_fips);
if (retval != 0)
goto error;
@@ -330,6 +448,18 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
if (retval != 0)
goto error;
+ if (msgauth_required) {
+ /*
+ * Calculate and replace the Message-Authenticator MAC. Per RFC 2869
+ * section 5.14, use the authenticator from the request, not from the
+ * response.
+ */
+ retval = calculate_mac(secret, pkt, pkt_auth(request),
+ pkt_attr(pkt) + 2);
+ if (retval != 0)
+ goto error;
+ }
+
/* Copy the attrset for future use. */
retval = packet_set_attrset(ctx, secret, pkt);
if (retval != 0)
@@ -343,6 +473,34 @@ error:
return retval;
}
+/* Verify the Message-Authenticator value in pkt, using the provided
+ * authenticator (which may be from pkt or from a corresponding request). */
+static krb5_error_code
+verify_msgauth(const char *secret, const krad_packet *pkt,
+ const uint8_t auth[AUTH_FIELD_SIZE])
+{
+ uint8_t mac[MD5_DIGEST_SIZE];
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+ const krb5_data *msgauth;
+ krb5_error_code retval;
+
+ msgauth = krad_packet_get_attr(pkt, msgauth_type, 0);
+ if (msgauth == NULL)
+ return ENODATA;
+
+ retval = calculate_mac(secret, pkt, auth, mac);
+ if (retval)
+ return retval;
+
+ if (msgauth->length != MD5_DIGEST_SIZE)
+ return EMSGSIZE;
+
+ if (k5_bcmp(mac, msgauth->data, MD5_DIGEST_SIZE) != 0)
+ return EBADMSG;
+
+ return 0;
+}
+
/* Decode a packet. */
static krb5_error_code
decode_packet(krb5_context ctx, const char *secret, const krb5_data *buffer,
@@ -394,21 +552,35 @@ krad_packet_decode_request(krb5_context ctx, const char *secret,
krad_packet **reqpkt)
{
const krad_packet *tmp = NULL;
+ krad_packet *req;
krb5_error_code retval;
- retval = decode_packet(ctx, secret, buffer, reqpkt);
- if (cb != NULL && retval == 0) {
+ retval = decode_packet(ctx, secret, buffer, &req);
+ if (retval)
+ return retval;
+
+ /* Verify Message-Authenticator if present. */
+ if (has_pkt_msgauth(req)) {
+ retval = verify_msgauth(secret, req, pkt_auth(req));
+ if (retval) {
+ krad_packet_free(req);
+ return retval;
+ }
+ }
+
+ if (cb != NULL) {
for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
if (pkt_id_get(*reqpkt) == pkt_id_get(tmp))
break;
}
- }
- if (cb != NULL && (retval != 0 || tmp != NULL))
- (*cb)(data, TRUE);
+ if (tmp != NULL)
+ (*cb)(data, TRUE);
+ }
+ *reqpkt = req;
*duppkt = tmp;
- return retval;
+ return 0;
}
krb5_error_code
@@ -435,9 +607,17 @@ krad_packet_decode_response(krb5_context ctx, const char *secret,
break;
}
- /* If the authenticator matches, then the response is valid. */
- if (memcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) == 0)
- break;
+ /* Verify the response authenticator. */
+ if (k5_bcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) != 0)
+ continue;
+
+ /* Verify Message-Authenticator if present. */
+ if (has_pkt_msgauth(*rsppkt)) {
+ if (verify_msgauth(secret, *rsppkt, pkt_auth(tmp)) != 0)
+ continue;
+ }
+
+ break;
}
}
diff --git a/src/lib/krad/t_attrset.c b/src/lib/krad/t_attrset.c
index 0f95762534..9a70529dc5 100644
--- a/src/lib/krad/t_attrset.c
+++ b/src/lib/krad/t_attrset.c
@@ -63,7 +63,7 @@ main()
noerror(krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp));
/* Encode attrset. */
- noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len,
+ noerror(kr_attrset_encode(set, "foo", auth, FALSE, buffer, &encode_len,
&is_fips));
krad_attrset_free(set);
diff --git a/src/lib/krad/t_daemon.py b/src/lib/krad/t_daemon.py
index 7668cd7f87..7fa0449a3c 100755
--- a/src/lib/krad/t_daemon.py
+++ b/src/lib/krad/t_daemon.py
@@ -40,6 +40,7 @@ DICTIONARY = """
ATTRIBUTE\tUser-Name\t1\tstring
ATTRIBUTE\tUser-Password\t2\toctets
ATTRIBUTE\tNAS-Identifier\t32\tstring
+ATTRIBUTE\tMessage-Authenticator\t80\toctets
"""
class TestServer(server.Server):
@@ -52,7 +53,7 @@ class TestServer(server.Server):
if key == "User-Password":
passwd = map(pkt.PwDecrypt, pkt[key])
- reply = self.CreateReplyPacket(pkt)
+ reply = self.CreateReplyPacket(pkt, message_authenticator=True)
if passwd == ['accept']:
reply.code = packet.AccessAccept
else:
diff --git a/src/lib/krad/t_packet.c b/src/lib/krad/t_packet.c
index c22489144f..104b6507a2 100644
--- a/src/lib/krad/t_packet.c
+++ b/src/lib/krad/t_packet.c
@@ -172,6 +172,9 @@ main(int argc, const char **argv)
krb5_data username, password;
krb5_boolean auth = FALSE;
krb5_context ctx;
+ const krad_packet *dupreq;
+ const krb5_data *encpkt;
+ krad_packet *decreq;
username = string2data("testUser");
@@ -184,9 +187,17 @@ main(int argc, const char **argv)
password = string2data("accept");
noerror(make_packet(ctx, &username, &password, &packets[ACCEPT_PACKET]));
+ encpkt = krad_packet_encode(packets[ACCEPT_PACKET]);
+ noerror(krad_packet_decode_request(ctx, "foo", encpkt, NULL, NULL,
+ &dupreq, &decreq));
+ krad_packet_free(decreq);
password = string2data("reject");
noerror(make_packet(ctx, &username, &password, &packets[REJECT_PACKET]));
+ encpkt = krad_packet_encode(packets[REJECT_PACKET]);
+ noerror(krad_packet_decode_request(ctx, "foo", encpkt, NULL, NULL,
+ &dupreq, &decreq));
+ krad_packet_free(decreq);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
diff --git a/src/tests/t_otp.py b/src/tests/t_otp.py
index cba871a0f2..1ec916598c 100755
--- a/src/tests/t_otp.py
+++ b/src/tests/t_otp.py
@@ -49,6 +49,7 @@ ATTRIBUTE User-Name 1 string
ATTRIBUTE User-Password 2 octets
ATTRIBUTE Service-Type 6 integer
ATTRIBUTE NAS-Identifier 32 string
+ATTRIBUTE Message-Authenticator 80 octets
'''
class RadiusDaemon(Process):
@@ -97,6 +98,8 @@ class RadiusDaemon(Process):
reply.code = packet.AccessReject
replyq['reply'] = False
+ reply.add_message_authenticator()
+
outq.put(replyq)
if addr is None:
sock.send(reply.ReplyPacket())
--
2.46.0

View File

@ -0,0 +1,38 @@
From 42e29f27ce64fece2839bcce910813e97ca31210 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Wed, 15 Jul 2020 15:42:20 -0400
Subject: [PATCH] Ignore bad enctypes in krb5_string_to_keysalts()
Fixes a problem where the presence of legacy/unrecognized keysalts in
supported_enctypes would prevent the kadmin programs from starting.
[ghudson@mit.edu: ideally we would put a warning in the kadmind log,
but that is difficult to do when the parsing is done inside a library.
Even adding a trace log is difficult because the kadm5 str_conv
functions do not accept contexts.]
ticket: 8929 (new)
(cherry picked from commit be5396ada0e8dabd68bd0aceb733cfca39a609bc)
(cherry picked from commit 3f873868fb08b77da2d30e164a0ef6c71c17c607)
---
src/lib/kadm5/str_conv.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/lib/kadm5/str_conv.c b/src/lib/kadm5/str_conv.c
index 7cf51d316..798295606 100644
--- a/src/lib/kadm5/str_conv.c
+++ b/src/lib/kadm5/str_conv.c
@@ -340,9 +340,10 @@ krb5_string_to_keysalts(const char *string, const char *tupleseps,
while ((ksp = strtok_r(p, tseps, &tlasts)) != NULL) {
/* Pass a null pointer to subsequent calls to strtok_r(). */
p = NULL;
- ret = string_to_keysalt(ksp, ksaltseps, &etype, &stype);
- if (ret)
- goto cleanup;
+
+ /* Discard unrecognized keysalts. */
+ if (string_to_keysalt(ksp, ksaltseps, &etype, &stype) != 0)
+ continue;
/* Ignore duplicate keysalts if caller asks. */
if (!dups && krb5_keysalt_is_present(ksalts, nksalts, etype, stype))

View File

@ -0,0 +1,92 @@
From b8bff4973a6642af80cbbc1bc03a52cb0d4e6247 Mon Sep 17 00:00:00 2001
From: Alexander Scheel <ascheel@redhat.com>
Date: Wed, 5 Jul 2017 11:38:30 -0400
Subject: [PATCH] Implement GSS_C_CHANNEL_BOUND_FLAG
Define a new channel-bound GSS return flag, and set it in the krb5
mech if the initiator sent channel bindings matching the acceptor's.
Do not error out if the acceptor specifies channel bindings and the
initiator does not send them.
[ghudson@mit.edu: simplified code changes; fleshed out commit message]
[iboukris: cherry-picked from another PR and reduced in scope]
ticket: 8899 (new)
(cherry picked from commit 429a31146083fac21958631c2af572b08ec91022)
(cherry picked from commit 3ea1d6296ced3a998e79356f9be212e4c5e6a5d5)
---
src/lib/gssapi/generic/gssapi_ext.h | 2 ++
src/lib/gssapi/krb5/accept_sec_context.c | 18 +++++++++++++-----
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h
index 218456e44..c675e8ebb 100644
--- a/src/lib/gssapi/generic/gssapi_ext.h
+++ b/src/lib/gssapi/generic/gssapi_ext.h
@@ -595,6 +595,8 @@ gss_store_cred_into(
* attribute (along with any applicable RFC 5587 attributes).
*/
+#define GSS_C_CHANNEL_BOUND_FLAG 2048 /* 0x00000800 */
+
OM_uint32 KRB5_CALLCONV
gssspi_query_meta_data(
OM_uint32 *minor_status,
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index 70dd7fc0c..9d3e2f4fe 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -427,6 +427,9 @@ kg_process_extension(krb5_context context,
GSS_C_SEQUENCE_FLAG | GSS_C_DCE_STYLE | \
GSS_C_IDENTIFY_FLAG | GSS_C_EXTENDED_ERROR_FLAG)
+/* A zero-value channel binding, for comparison */
+static const uint8_t null_cb[CB_MD5_LEN];
+
/*
* The krb5 GSS mech appropriates the authenticator checksum field from RFC
* 4120 to store structured data instead of a checksum, indicated with checksum
@@ -435,9 +438,10 @@ kg_process_extension(krb5_context context,
*
* Interpret the checksum. Read delegated creds into *deleg_out if it is not
* NULL. Set *flags_out to the allowed subset of token flags, plus
- * GSS_C_DELEG_FLAG if a delegated credential was present. Process any
- * extensions found using exts. On error, set *code_out to a krb5_error code
- * for use as a minor status value.
+ * GSS_C_DELEG_FLAG if a delegated credential was present and
+ * GSS_C_CHANNEL_BOUND_FLAG if matching channel bindings are present. Process
+ * any extensions found using exts. On error, set *code_out to a krb5_error
+ * code for use as a minor status value.
*/
static OM_uint32
process_checksum(OM_uint32 *minor_status, krb5_context context,
@@ -450,7 +454,7 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
krb5_error_code code = 0;
OM_uint32 status, option_id, token_flags;
size_t cb_len, option_len;
- krb5_boolean valid;
+ krb5_boolean valid, token_cb_present = FALSE, cb_match = FALSE;
krb5_key subkey;
krb5_data option, empty = empty_data();
krb5_checksum cb_cksum;
@@ -516,7 +520,9 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
goto fail;
}
assert(cb_cksum.length == cb_len);
- if (k5_bcmp(token_cb, cb_cksum.contents, cb_len) != 0) {
+ token_cb_present = (k5_bcmp(token_cb, null_cb, cb_len) != 0);
+ cb_match = (k5_bcmp(token_cb, cb_cksum.contents, cb_len) == 0);
+ if (token_cb_present && !cb_match) {
status = GSS_S_BAD_BINDINGS;
goto fail;
}
@@ -525,6 +531,8 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
/* Read the token flags and accept some of them as context flags. */
token_flags = k5_input_get_uint32_le(&in);
*flags_out = token_flags & INITIATOR_FLAGS;
+ if (cb_match)
+ *flags_out |= GSS_C_CHANNEL_BOUND_FLAG;
/* Read the delegated credential if present. */
if (in.len >= 4 && (token_flags & GSS_C_DELEG_FLAG)) {

View File

@ -0,0 +1,103 @@
From b37714a1b9138c0258d357931215fbd5ca7fa72b Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Mon, 9 Mar 2020 16:04:21 +0100
Subject: [PATCH] Implement KERB_AP_OPTIONS_CBT (server side)
Add server support for Microsoft's KERB_AP_OPTIONS_CBT as described in
MS-KILE. If the client includes the AP option in the authenticator
authdata and the server passed channel bindings, require the bindings
to match.
[ghudson@mit.edu: refactored to put more logic in the helper function;
added a comment; clarified commit message]
ticket: 8900 (new)
(cherry picked from commit 4f7c77b64a048ca5e3199b26b31493698c777a9c)
(cherry picked from commit 6407bf087fe53088d91efd09df736e979cd4e8db)
---
src/include/krb5/krb5.hin | 1 +
src/lib/gssapi/krb5/accept_sec_context.c | 45 +++++++++++++++++++++++-
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index f8269fb17..9264bede1 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -1915,6 +1915,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
#define KRB5_AUTHDATA_SIGNTICKET 512 /**< formerly 142 in krb5 1.8 */
#define KRB5_AUTHDATA_FX_ARMOR 71
#define KRB5_AUTHDATA_AUTH_INDICATOR 97
+#define KRB5_AUTHDATA_AP_OPTIONS 143
/** @} */ /* end of KRB5_AUTHDATA group */
/* password change constants */
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index 9d3e2f4fe..175a24c4e 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -430,6 +430,32 @@ kg_process_extension(krb5_context context,
/* A zero-value channel binding, for comparison */
static const uint8_t null_cb[CB_MD5_LEN];
+/* Look for AP_OPTIONS in authdata. If present and the options include
+ * KERB_AP_OPTIONS_CBT, set *cbt_out to true. */
+static krb5_error_code
+check_cbt(krb5_context context, krb5_authdata **authdata,
+ krb5_boolean *cbt_out)
+{
+ krb5_error_code code;
+ uint32_t ad_ap_options;
+ const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
+
+ *cbt_out = FALSE;
+
+ code = krb5_find_authdata(context, NULL, authdata,
+ KRB5_AUTHDATA_AP_OPTIONS, &authdata);
+ if (code || authdata == NULL)
+ return code;
+ if (authdata[1] != NULL || authdata[0]->length != 4)
+ return KRB5KRB_AP_ERR_MSG_TYPE;
+
+ ad_ap_options = load_32_le(authdata[0]->contents);
+ if (ad_ap_options & KERB_AP_OPTIONS_CBT)
+ *cbt_out = TRUE;
+
+ return 0;
+}
+
/*
* The krb5 GSS mech appropriates the authenticator checksum field from RFC
* 4120 to store structured data instead of a checksum, indicated with checksum
@@ -454,7 +480,7 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
krb5_error_code code = 0;
OM_uint32 status, option_id, token_flags;
size_t cb_len, option_len;
- krb5_boolean valid, token_cb_present = FALSE, cb_match = FALSE;
+ krb5_boolean valid, client_cbt, token_cb_present = FALSE, cb_match = FALSE;
krb5_key subkey;
krb5_data option, empty = empty_data();
krb5_checksum cb_cksum;
@@ -582,6 +608,23 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
}
}
+ /*
+ * If the client asserts the KERB_AP_OPTIONS_CBT flag (from MS-KILE) in the
+ * authenticator authdata, and the acceptor passed channel bindings,
+ * require matching channel bindings from the client. The intent is to
+ * prevent an authenticator generated for use outside of a TLS channel from
+ * being used inside of one.
+ */
+ code = check_cbt(context, authenticator->authorization_data, &client_cbt);
+ if (code) {
+ status = GSS_S_FAILURE;
+ goto fail;
+ }
+ if (client_cbt && acceptor_cb != GSS_C_NO_CHANNEL_BINDINGS && !cb_match) {
+ status = GSS_S_BAD_BINDINGS;
+ goto fail;
+ }
+
status = GSS_S_COMPLETE;
fail:

View File

@ -1,149 +0,0 @@
From 3d651a6e234bed4c4d4865a56c5fa47dab89a5a6 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 26 Mar 2018 11:12:39 -0400
Subject: [PATCH] Implement k5_buf_init_dynamic_zap
Add a variant of dynamic k5buf objects which zeroes memory when
reallocating or freeing the buffer.
(cherry picked from commit 8ee8246c14702dc03b02e31b9fb5b7c2bb674bfb)
---
src/include/k5-buf.h | 6 ++-
src/util/support/k5buf.c | 41 +++++++++++++++----
src/util/support/libkrb5support-fixed.exports | 1 +
3 files changed, 39 insertions(+), 9 deletions(-)
diff --git a/src/include/k5-buf.h b/src/include/k5-buf.h
index 1223916a6..48e2a7d53 100644
--- a/src/include/k5-buf.h
+++ b/src/include/k5-buf.h
@@ -45,7 +45,7 @@
*/
/* Buffer type values */
-enum k5buftype { K5BUF_ERROR, K5BUF_FIXED, K5BUF_DYNAMIC };
+enum k5buftype { K5BUF_ERROR, K5BUF_FIXED, K5BUF_DYNAMIC, K5BUF_DYNAMIC_ZAP };
struct k5buf {
enum k5buftype buftype;
@@ -63,6 +63,10 @@ void k5_buf_init_fixed(struct k5buf *buf, char *data, size_t space);
/* Initialize a k5buf using an internally allocated dynamic buffer. */
void k5_buf_init_dynamic(struct k5buf *buf);
+/* Initialize a k5buf using an internally allocated dynamic buffer, zeroing
+ * memory when reallocating or freeing. */
+void k5_buf_init_dynamic_zap(struct k5buf *buf);
+
/* Add a C string to BUF. */
void k5_buf_add(struct k5buf *buf, const char *data);
diff --git a/src/util/support/k5buf.c b/src/util/support/k5buf.c
index 35978f238..b2b5e5b67 100644
--- a/src/util/support/k5buf.c
+++ b/src/util/support/k5buf.c
@@ -37,7 +37,7 @@
/*
* Structure invariants:
*
- * buftype is K5BUF_FIXED, K5BUF_DYNAMIC, or K5BUF_ERROR
+ * buftype is K5BUF_FIXED, K5BUF_DYNAMIC, K5BUF_DYNAMIC_ZAP, or K5BUF_ERROR
* if buftype is K5BUF_ERROR, the other fields are NULL or 0
* if buftype is not K5BUF_ERROR:
* space > 0
@@ -77,22 +77,35 @@ ensure_space(struct k5buf *buf, size_t len)
return 1;
if (buf->buftype == K5BUF_FIXED) /* Can't resize a fixed buffer. */
goto error_exit;
- assert(buf->buftype == K5BUF_DYNAMIC);
+ assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);
new_space = buf->space * 2;
while (new_space - buf->len - 1 < len) {
if (new_space > SIZE_MAX / 2)
goto error_exit;
new_space *= 2;
}
- new_data = realloc(buf->data, new_space);
- if (new_data == NULL)
- goto error_exit;
+ if (buf->buftype == K5BUF_DYNAMIC_ZAP) {
+ /* realloc() could leave behind a partial copy of sensitive data. */
+ new_data = malloc(new_space);
+ if (new_data == NULL)
+ goto error_exit;
+ memcpy(new_data, buf->data, buf->len);
+ new_data[buf->len] = '\0';
+ zap(buf->data, buf->len);
+ free(buf->data);
+ } else {
+ new_data = realloc(buf->data, new_space);
+ if (new_data == NULL)
+ goto error_exit;
+ }
buf->data = new_data;
buf->space = new_space;
return 1;
error_exit:
- if (buf->buftype == K5BUF_DYNAMIC)
+ if (buf->buftype == K5BUF_DYNAMIC_ZAP)
+ zap(buf->data, buf->len);
+ if (buf->buftype == K5BUF_DYNAMIC_ZAP || buf->buftype == K5BUF_DYNAMIC)
free(buf->data);
set_error(buf);
return 0;
@@ -123,6 +136,14 @@ k5_buf_init_dynamic(struct k5buf *buf)
*endptr(buf) = '\0';
}
+void
+k5_buf_init_dynamic_zap(struct k5buf *buf)
+{
+ k5_buf_init_dynamic(buf);
+ if (buf->buftype == K5BUF_DYNAMIC)
+ buf->buftype = K5BUF_DYNAMIC_ZAP;
+}
+
void
k5_buf_add(struct k5buf *buf, const char *data)
{
@@ -163,7 +184,7 @@ k5_buf_add_vfmt(struct k5buf *buf, const char *fmt, va_list ap)
}
/* Optimistically format the data directly into the dynamic buffer. */
- assert(buf->buftype == K5BUF_DYNAMIC);
+ assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);
va_copy(apcopy, ap);
r = vsnprintf(endptr(buf), remaining, fmt, apcopy);
va_end(apcopy);
@@ -197,6 +218,8 @@ k5_buf_add_vfmt(struct k5buf *buf, const char *fmt, va_list ap)
memcpy(endptr(buf), tmp, r + 1);
buf->len += r;
}
+ if (buf->buftype == K5BUF_DYNAMIC_ZAP)
+ zap(tmp, strlen(tmp));
free(tmp);
}
@@ -241,7 +264,9 @@ k5_buf_free(struct k5buf *buf)
{
if (buf->buftype == K5BUF_ERROR)
return;
- assert(buf->buftype == K5BUF_DYNAMIC);
+ assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);
+ if (buf->buftype == K5BUF_DYNAMIC_ZAP)
+ zap(buf->data, buf->len);
free(buf->data);
set_error(buf);
}
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index cb9bf0826..a5e2ade04 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -3,6 +3,7 @@ k5_base64_encode
k5_bcmp
k5_buf_init_fixed
k5_buf_init_dynamic
+k5_buf_init_dynamic_zap
k5_buf_add
k5_buf_add_len
k5_buf_add_fmt

View File

@ -0,0 +1,31 @@
From 01b93a5522fd0e402401bf6ed3c1ebfde613965e Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Tue, 9 Jun 2020 16:23:37 -0400
Subject: [PATCH] Improve negoex_parse_token() code hygiene
If the while loop in negoex_parse_token() runs for zero iterations,
major will be used initialized. Currently this cannot happen, but
only because both of the call sites check for zero-length tokens.
Initialize major for safety.
[ghudson@mit.edu: rewrote commit message]
(cherry picked from commit 4f91b6f8fa6fe1de662b3fdac0d59b7758ec642a)
(cherry picked from commit c726a72c68244129eb08b840b92144acfa776573)
---
src/lib/gssapi/spnego/negoex_util.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib/gssapi/spnego/negoex_util.c b/src/lib/gssapi/spnego/negoex_util.c
index 700368456..99580fd79 100644
--- a/src/lib/gssapi/spnego/negoex_util.c
+++ b/src/lib/gssapi/spnego/negoex_util.c
@@ -454,7 +454,7 @@ negoex_parse_token(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
gss_const_buffer_t token,
struct negoex_message **messages_out, size_t *count_out)
{
- OM_uint32 major;
+ OM_uint32 major = GSS_S_COMPLETE;
size_t count = 0;
struct k5input in;
struct negoex_message *messages = NULL, *newptr;

View File

@ -1,38 +0,0 @@
From bbc68d1657306a61a7646dd7b9690f67705e24be Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 3 Jan 2018 11:59:14 -0500
Subject: [PATCH] Include etype-info in for hardware preauth hints
If a principal has the requires_hwauth bit set, include PA-ETYPE-INFO
or PA-ETYPE-INFO2 padata in the PREAUTH_REQUIRED error, as preauth
mechs involving hardware tokens may also use the principal's Kerberos
password.
ticket: 8629
(cherry picked from commit ba92da05accc524b8037453b63ced1a6c65fd2a1)
---
src/kdc/kdc_preauth.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 81d0b8cff..739c5e776 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -144,7 +144,7 @@ static preauth_system static_preauth_systems[] = {
{
"etype-info",
KRB5_PADATA_ETYPE_INFO,
- 0,
+ PA_HARDWARE,
NULL,
NULL,
NULL,
@@ -155,7 +155,7 @@ static preauth_system static_preauth_systems[] = {
{
"etype-info2",
KRB5_PADATA_ETYPE_INFO2,
- 0,
+ PA_HARDWARE,
NULL,
NULL,
NULL,

View File

@ -1,514 +0,0 @@
From b623881ec039bffc758f53906f7e4f9b884f1cf4 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 15 Mar 2018 14:37:28 -0400
Subject: [PATCH] Include preauth name in trace output if possible
Add a {patype} trace format specifier for a single pa-type value. Add
a krb5_preauthtype to string conversion function to trace machinery
and use it when formatting {patype} or {patypes}.
[ghudson@mit.edu: wrote conversion function; edited commit message]
ticket: 8653 (new)
(cherry picked from commit 9c68fe39b018666eabe033b639c1f35d03ba51c7)
---
src/include/k5-trace.h | 17 +--
src/lib/krb5/os/t_trace.ref | 2 +-
src/lib/krb5/os/trace.c | 61 +++++++++-
src/tests/t_pkinit.py | 43 +++----
src/tests/t_preauth.py | 216 ++++++++++++++++++------------------
5 files changed, 200 insertions(+), 139 deletions(-)
diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
index 390a8b7d6..5f7eb9517 100644
--- a/src/include/k5-trace.h
+++ b/src/include/k5-trace.h
@@ -75,6 +75,7 @@
* {cksum} const krb5_checksum *, display cksumtype and hex checksum
* {princ} krb5_principal, unparse and display
* {ptype} krb5_int32, krb5_principal type, display name
+ * {patype} krb5_preauthtype, a single padata type number
* {patypes} krb5_pa_data **, display list of padata type numbers
* {etype} krb5_enctype, display shortest name of enctype
* {etypes} krb5_enctype *, display list of enctypes
@@ -232,14 +233,14 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
#define TRACE_INIT_CREDS_PREAUTH_DECRYPT_FAIL(c, code) \
TRACE(c, "Decrypt with preauth AS key failed: {kerr}", code)
#define TRACE_INIT_CREDS_PREAUTH_MORE(c, patype) \
- TRACE(c, "Continuing preauth mech {int}", (int)patype)
+ TRACE(c, "Continuing preauth mech {patype}", patype)
#define TRACE_INIT_CREDS_PREAUTH_NONE(c) \
TRACE(c, "Sending unauthenticated request")
#define TRACE_INIT_CREDS_PREAUTH_OPTIMISTIC(c) \
TRACE(c, "Attempting optimistic preauth")
#define TRACE_INIT_CREDS_PREAUTH_TRYAGAIN(c, patype, code) \
- TRACE(c, "Recovering from KDC error {int} using preauth mech {int}", \
- (int)patype, (int)code)
+ TRACE(c, "Recovering from KDC error {int} using preauth mech {patype}", \
+ patype, (int)code)
#define TRACE_INIT_CREDS_RESTART_FAST(c) \
TRACE(c, "Restarting to upgrade to FAST")
#define TRACE_INIT_CREDS_RESTART_PREAUTH_FAILED(c) \
@@ -290,7 +291,7 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
#define TRACE_PREAUTH_CONFLICT(c, name1, name2, patype) \
TRACE(c, "Preauth module {str} conflicts with module {str} for pa " \
- "type {int}", name1, name2, (int) patype)
+ "type {patype}", name1, name2, patype)
#define TRACE_PREAUTH_COOKIE(c, len, data) \
TRACE(c, "Received cookie: {lenstr}", (size_t) len, data)
#define TRACE_PREAUTH_ENC_TS_KEY_GAK(c, keyblock) \
@@ -302,8 +303,8 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
TRACE(c, "Selected etype info: etype {etype}, salt \"{data}\", " \
"params \"{data}\"", etype, salt, s2kparams)
#define TRACE_PREAUTH_INFO_FAIL(c, patype, code) \
- TRACE(c, "Preauth builtin info function failure, type={int}: {kerr}", \
- (int) patype, code)
+ TRACE(c, "Preauth builtin info function failure, type={patype}: {kerr}", \
+ patype, code)
#define TRACE_PREAUTH_INPUT(c, padata) \
TRACE(c, "Processing preauth types: {patypes}", padata)
#define TRACE_PREAUTH_OUTPUT(c, padata) \
@@ -314,8 +315,8 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
#define TRACE_PREAUTH_SAM_KEY_GAK(c, keyblock) \
TRACE(c, "AS key obtained for SAM: {keyblock}", keyblock)
#define TRACE_PREAUTH_SALT(c, salt, patype) \
- TRACE(c, "Received salt \"{data}\" via padata type {int}", salt, \
- (int) patype)
+ TRACE(c, "Received salt \"{data}\" via padata type {patype}", salt, \
+ patype)
#define TRACE_PREAUTH_SKIP(c, name, patype) \
TRACE(c, "Skipping previously used preauth module {str} ({int})", \
name, (int) patype)
diff --git a/src/lib/krb5/os/t_trace.ref b/src/lib/krb5/os/t_trace.ref
index ca5818a1e..bd5d9b6b6 100644
--- a/src/lib/krb5/os/t_trace.ref
+++ b/src/lib/krb5/os/t_trace.ref
@@ -38,7 +38,7 @@ int, krb5_principal type: Windows 2000 UPN and SID
int, krb5_principal type: NT 4 style name
int, krb5_principal type: NT 4 style name and SID
int, krb5_principal type: ?
-krb5_pa_data **, display list of padata type numbers: 3, 0
+krb5_pa_data **, display list of padata type numbers: PA-PW-SALT (3), 0
krb5_pa_data **, display list of padata type numbers: (empty)
krb5_enctype, display shortest name of enctype: des-cbc-crc
krb5_enctype *, display list of enctypes: 5, rc4-hmac-exp, 511
diff --git a/src/lib/krb5/os/trace.c b/src/lib/krb5/os/trace.c
index 779f184cb..10b4f0c14 100644
--- a/src/lib/krb5/os/trace.c
+++ b/src/lib/krb5/os/trace.c
@@ -123,6 +123,50 @@ principal_type_string(krb5_int32 type)
}
}
+static char *
+padata_type_string(krb5_preauthtype type)
+{
+ switch (type) {
+ case KRB5_PADATA_TGS_REQ: return "PA-TGS-REQ";
+ case KRB5_PADATA_ENC_TIMESTAMP: return "PA-ENC-TIMESTAMP";
+ case KRB5_PADATA_PW_SALT: return "PA-PW-SALT";
+ case KRB5_PADATA_ENC_UNIX_TIME: return "PA-ENC-UNIX-TIME";
+ case KRB5_PADATA_ENC_SANDIA_SECURID: return "PA-SANDIA-SECUREID";
+ case KRB5_PADATA_SESAME: return "PA-SESAME";
+ case KRB5_PADATA_OSF_DCE: return "PA-OSF-DCE";
+ case KRB5_CYBERSAFE_SECUREID: return "PA-CYBERSAFE-SECUREID";
+ case KRB5_PADATA_AFS3_SALT: return "PA-AFS3-SALT";
+ case KRB5_PADATA_ETYPE_INFO: return "PA-ETYPE-INFO";
+ case KRB5_PADATA_SAM_CHALLENGE: return "PA-SAM-CHALLENGE";
+ case KRB5_PADATA_SAM_RESPONSE: return "PA-SAM-RESPONSE";
+ case KRB5_PADATA_PK_AS_REQ_OLD: return "PA-PK-AS-REQ_OLD";
+ case KRB5_PADATA_PK_AS_REP_OLD: return "PA-PK-AS-REP_OLD";
+ case KRB5_PADATA_PK_AS_REQ: return "PA-PK-AS-REQ";
+ case KRB5_PADATA_PK_AS_REP: return "PA-PK-AS-REP";
+ case KRB5_PADATA_ETYPE_INFO2: return "PA-ETYPE-INFO2";
+ case KRB5_PADATA_SVR_REFERRAL_INFO: return "PA-SVR-REFERRAL-INFO";
+ case KRB5_PADATA_SAM_REDIRECT: return "PA-SAM-REDIRECT";
+ case KRB5_PADATA_GET_FROM_TYPED_DATA: return "PA-GET-FROM-TYPED-DATA";
+ case KRB5_PADATA_SAM_CHALLENGE_2: return "PA-SAM-CHALLENGE2";
+ case KRB5_PADATA_SAM_RESPONSE_2: return "PA-SAM-RESPONSE2";
+ case KRB5_PADATA_PAC_REQUEST: return "PA-PAC-REQUEST";
+ case KRB5_PADATA_FOR_USER: return "PA-FOR_USER";
+ case KRB5_PADATA_S4U_X509_USER: return "PA-FOR-X509-USER";
+ case KRB5_PADATA_AS_CHECKSUM: return "PA-AS-CHECKSUM";
+ case KRB5_PADATA_FX_COOKIE: return "PA-FX-COOKIE";
+ case KRB5_PADATA_FX_FAST: return "PA-FX-FAST";
+ case KRB5_PADATA_FX_ERROR: return "PA-FX-ERROR";
+ case KRB5_PADATA_ENCRYPTED_CHALLENGE: return "PA-ENCRYPTED-CHALLENGE";
+ case KRB5_PADATA_OTP_CHALLENGE: return "PA-OTP-CHALLENGE";
+ case KRB5_PADATA_OTP_REQUEST: return "PA-OTP-REQUEST";
+ case KRB5_PADATA_OTP_PIN_CHANGE: return "PA-OTP-PIN-CHANGE";
+ case KRB5_PADATA_PKINIT_KX: return "PA-PKINIT-KX";
+ case KRB5_ENCPADATA_REQ_ENC_PA_REP: return "PA-REQ-ENC-PA-REP";
+ case KRB5_PADATA_AS_FRESHNESS: return "PA_AS_FRESHNESS";
+ default: return NULL;
+ }
+}
+
static char *
trace_format(krb5_context context, const char *fmt, va_list ap)
{
@@ -140,6 +184,8 @@ trace_format(krb5_context context, const char *fmt, va_list ap)
krb5_key key;
const krb5_checksum *cksum;
krb5_pa_data **padata;
+ krb5_preauthtype pa_type;
+ const char *name;
krb5_ccache ccache;
krb5_keytab keytab;
krb5_creds *creds;
@@ -271,10 +317,23 @@ trace_format(krb5_context context, const char *fmt, va_list ap)
if (padata == NULL || *padata == NULL)
k5_buf_add(&buf, "(empty)");
for (; padata != NULL && *padata != NULL; padata++) {
- k5_buf_add_fmt(&buf, "%d", (int)(*padata)->pa_type);
+ pa_type = (*padata)->pa_type;
+ name = padata_type_string(pa_type);
+ if (name != NULL)
+ k5_buf_add_fmt(&buf, "%s (%d)", name, (int)pa_type);
+ else
+ k5_buf_add_fmt(&buf, "%d", (int)pa_type);
+
if (*(padata + 1) != NULL)
k5_buf_add(&buf, ", ");
}
+ } else if (strcmp(tmpbuf, "patype") == 0) {
+ pa_type = va_arg(ap, krb5_preauthtype);
+ name = padata_type_string(pa_type);
+ if (name != NULL)
+ k5_buf_add_fmt(&buf, "%s (%d)", name, (int)pa_type);
+ else
+ k5_buf_add_fmt(&buf, "%d", (int)pa_type);
} else if (strcmp(tmpbuf, "etype") == 0) {
etype = va_arg(ap, krb5_enctype);
if (krb5_enctype_to_name(etype, TRUE, tmpbuf, sizeof(tmpbuf)) == 0)
diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py
index 5bc60cb1e..0e964c689 100755
--- a/src/tests/t_pkinit.py
+++ b/src/tests/t_pkinit.py
@@ -164,18 +164,19 @@ realm.stop_kdc()
realm.start_kdc()
# Run the basic test - PKINIT with FILE: identity, with no password on the key.
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'PKINIT client received freshness token from KDC',
+ 'PKINIT loading CA certs and CRLs from FILE',
+ 'PKINIT client making DH request',
+ ' preauth for next request: PA-FX-COOKIE (133), PA-PK-AS-REQ (16)',
+ 'PKINIT client verified DH reply',
+ 'PKINIT client found id-pkinit-san in KDC cert',
+ 'PKINIT client matched KDC principal krbtgt/')
realm.kinit(realm.user_princ,
flags=['-X', 'X509_user_identity=%s' % file_identity],
- expected_trace=('Sending unauthenticated request',
- '/Additional pre-authentication required',
- 'Preauthenticating using KDC method data',
- 'PKINIT client received freshness token from KDC',
- 'PKINIT loading CA certs and CRLs from FILE',
- 'PKINIT client making DH request',
- 'Produced preauth for next request: 133, 16',
- 'PKINIT client verified DH reply',
- 'PKINIT client found id-pkinit-san in KDC cert',
- 'PKINIT client matched KDC principal krbtgt/'))
+ expected_trace=msgs)
realm.klist(realm.user_princ)
realm.run([kvno, realm.host_princ])
@@ -194,19 +195,19 @@ minbits_kdc_conf = {'realms': {'$realm': {'pkinit_dh_min_bits': '4096'}}}
minbits_env = realm.special_env('restrict', True, kdc_conf=minbits_kdc_conf)
realm.stop_kdc()
realm.start_kdc(env=minbits_env)
-expected_trace = ('Sending unauthenticated request',
- '/Additional pre-authentication required',
- 'Preauthenticating using KDC method data',
- 'Preauth module pkinit (16) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, 16',
- '/Key parameters not accepted',
- 'Preauth tryagain input types (16): 109, 133',
- 'trying again with KDC-provided parameters',
- 'Preauth module pkinit (16) tryagain returned: 0/Success',
- 'Followup preauth for next request: 16, 133')
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Preauth module pkinit (16) (real) returned: 0/Success',
+ ' preauth for next request: PA-FX-COOKIE (133), PA-PK-AS-REQ (16)',
+ '/Key parameters not accepted',
+ 'Preauth tryagain input types (16): 109, PA-FX-COOKIE (133)',
+ 'trying again with KDC-provided parameters',
+ 'Preauth module pkinit (16) tryagain returned: 0/Success',
+ ' preauth for next request: PA-PK-AS-REQ (16), PA-FX-COOKIE (133)')
realm.kinit(realm.user_princ,
flags=['-X', 'X509_user_identity=%s' % file_identity],
- expected_trace=expected_trace)
+ expected_trace=msgs)
# Test enforcement of required freshness tokens. (We can leave
# freshness tokens required after this test.)
diff --git a/src/tests/t_preauth.py b/src/tests/t_preauth.py
index fec0bf619..efb3ea20d 100644
--- a/src/tests/t_preauth.py
+++ b/src/tests/t_preauth.py
@@ -18,15 +18,15 @@ realm.kinit('nokeyuser', password('user'), expected_code=1,
# PA-FX-COOKIE; 2 is encrypted timestamp.
# Test normal preauth flow.
-expected_trace = ('Sending unauthenticated request',
- '/Additional pre-authentication required',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, -123',
- 'Decrypted AS reply')
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ 'Decrypted AS reply')
realm.run(['./icred', realm.user_princ, password('user')],
- expected_msg='testval', expected_trace=expected_trace)
+ expected_msg='testval', expected_trace=msgs)
# Test successful optimistic preauth.
expected_trace = ('Attempting optimistic preauth',
@@ -39,136 +39,136 @@ realm.run(['./icred', '-o', '-123', realm.user_princ, password('user')],
# Test optimistic preauth failing on client, followed by successful
# preauth using the same module.
-expected_trace = ('Attempting optimistic preauth',
- 'Processing preauth types: -123',
- '/induced optimistic fail',
- 'Sending unauthenticated request',
- '/Additional pre-authentication required',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, -123',
- 'Decrypted AS reply')
+msgs = ('Attempting optimistic preauth',
+ 'Processing preauth types: -123',
+ '/induced optimistic fail',
+ 'Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ 'Decrypted AS reply')
realm.run(['./icred', '-o', '-123', '-X', 'fail_optimistic', realm.user_princ,
password('user')], expected_msg='testval',
- expected_trace=expected_trace)
+ expected_trace=msgs)
# Test optimistic preauth failing on KDC, followed by successful preauth
# using the same module.
realm.run([kadminl, 'setstr', realm.user_princ, 'failopt', 'yes'])
-expected_trace = ('Attempting optimistic preauth',
- 'Processing preauth types: -123',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: -123',
- '/Preauthentication failed',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, -123',
- 'Decrypted AS reply')
+msgs = ('Attempting optimistic preauth',
+ 'Processing preauth types: -123',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: -123',
+ '/Preauthentication failed',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ 'Decrypted AS reply')
realm.run(['./icred', '-o', '-123', realm.user_princ, password('user')],
- expected_msg='testval', expected_trace=expected_trace)
+ expected_msg='testval', expected_trace=msgs)
realm.run([kadminl, 'delstr', realm.user_princ, 'failopt'])
# Test KDC_ERR_MORE_PREAUTH_DATA_REQUIRED and secure cookies.
realm.run([kadminl, 'setstr', realm.user_princ, '2rt', 'secondtrip'])
-expected_trace = ('Sending unauthenticated request',
- '/Additional pre-authentication required',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, -123',
- '/More preauthentication data is required',
- 'Continuing preauth mech -123',
- 'Processing preauth types: -123, 133',
- 'Produced preauth for next request: 133, -123',
- 'Decrypted AS reply')
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/More preauthentication data is required',
+ 'Continuing preauth mech -123',
+ 'Processing preauth types: -123, PA-FX-COOKIE (133)',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ 'Decrypted AS reply')
realm.run(['./icred', realm.user_princ, password('user')],
- expected_msg='2rt: secondtrip', expected_trace=expected_trace)
+ expected_msg='2rt: secondtrip', expected_trace=msgs)
# Test client-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
# falling back to encrypted timestamp.
-expected_trace = ('Sending unauthenticated request',
- '/Additional pre-authentication required',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, -123',
- '/More preauthentication data is required',
- 'Continuing preauth mech -123',
- 'Processing preauth types: -123, 133',
- '/induced 2rt fail',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Encrypted timestamp (for ',
- 'module encrypted_timestamp (2) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, 2',
- 'Decrypted AS reply')
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/More preauthentication data is required',
+ 'Continuing preauth mech -123',
+ 'Processing preauth types: -123, PA-FX-COOKIE (133)',
+ '/induced 2rt fail',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Encrypted timestamp (for ',
+ 'module encrypted_timestamp (2) (real) returned: 0/Success',
+ 'preauth for next request: PA-FX-COOKIE (133), PA-ENC-TIMESTAMP (2)',
+ 'Decrypted AS reply')
realm.run(['./icred', '-X', 'fail_2rt', realm.user_princ, password('user')],
- expected_msg='2rt: secondtrip', expected_trace=expected_trace)
+ expected_msg='2rt: secondtrip', expected_trace=msgs)
# Test KDC-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
# falling back to encrypted timestamp.
realm.run([kadminl, 'setstr', realm.user_princ, 'fail2rt', 'yes'])
-expected_trace = ('Sending unauthenticated request',
- '/Additional pre-authentication required',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, -123',
- '/More preauthentication data is required',
- 'Continuing preauth mech -123',
- 'Processing preauth types: -123, 133',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, -123',
- '/Preauthentication failed',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Encrypted timestamp (for ',
- 'module encrypted_timestamp (2) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, 2',
- 'Decrypted AS reply')
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/More preauthentication data is required',
+ 'Continuing preauth mech -123',
+ 'Processing preauth types: -123, PA-FX-COOKIE (133)',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/Preauthentication failed',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Encrypted timestamp (for ',
+ 'module encrypted_timestamp (2) (real) returned: 0/Success',
+ 'preauth for next request: PA-FX-COOKIE (133), PA-ENC-TIMESTAMP (2)',
+ 'Decrypted AS reply')
realm.run(['./icred', realm.user_princ, password('user')],
- expected_msg='2rt: secondtrip', expected_trace=expected_trace)
+ expected_msg='2rt: secondtrip', expected_trace=msgs)
realm.run([kadminl, 'delstr', realm.user_princ, 'fail2rt'])
# Test tryagain flow by inducing a KDC_ERR_ENCTYPE_NOSUPP error on the KDC.
realm.run([kadminl, 'setstr', realm.user_princ, 'err', 'testagain'])
-expected_trace = ('Sending unauthenticated request',
- '/Additional pre-authentication required',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, -123',
- '/KDC has no support for encryption type',
- 'Recovering from KDC error 14 using preauth mech -123',
- 'Preauth tryagain input types (-123): -123, 133',
- 'Preauth module test (-123) tryagain returned: 0/Success',
- 'Followup preauth for next request: -123, 133',
- 'Decrypted AS reply')
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/KDC has no support for encryption type',
+ 'Recovering from KDC error 14 using preauth mech -123',
+ 'Preauth tryagain input types (-123): -123, PA-FX-COOKIE (133)',
+ 'Preauth module test (-123) tryagain returned: 0/Success',
+ 'Followup preauth for next request: -123, PA-FX-COOKIE (133)',
+ 'Decrypted AS reply')
realm.run(['./icred', realm.user_princ, password('user')],
- expected_msg='tryagain: testagain', expected_trace=expected_trace)
+ expected_msg='tryagain: testagain', expected_trace=msgs)
# Test a client-side tryagain failure, falling back to encrypted
# timestamp.
-expected_trace = ('Sending unauthenticated request',
- '/Additional pre-authentication required',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, -123',
- '/KDC has no support for encryption type',
- 'Recovering from KDC error 14 using preauth mech -123',
- 'Preauth tryagain input types (-123): -123, 133',
- '/induced tryagain fail',
- 'Preauthenticating using KDC method data',
- 'Processing preauth types:',
- 'Encrypted timestamp (for ',
- 'module encrypted_timestamp (2) (real) returned: 0/Success',
- 'Produced preauth for next request: 133, 2',
- 'Decrypted AS reply')
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/KDC has no support for encryption type',
+ 'Recovering from KDC error 14 using preauth mech -123',
+ 'Preauth tryagain input types (-123): -123, PA-FX-COOKIE (133)',
+ '/induced tryagain fail',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Encrypted timestamp (for ',
+ 'module encrypted_timestamp (2) (real) returned: 0/Success',
+ 'preauth for next request: PA-FX-COOKIE (133), PA-ENC-TIMESTAMP (2)',
+ 'Decrypted AS reply')
realm.run(['./icred', '-X', 'fail_tryagain', realm.user_princ,
- password('user')], expected_trace=expected_trace)
+ password('user')], expected_trace=msgs)
# Test that multiple stepwise initial creds operations can be
# performed with the same krb5_context, with proper tracking of

View File

@ -1,35 +0,0 @@
From 9dd3a84f324979c29e8ab4b472e98dfa73e6b290 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Mon, 7 May 2018 16:42:59 -0400
Subject: [PATCH] Log when non-root ksu authorization fails
If non-root user attempts to ksu but is denied by policy, log to
syslog at LOG_WARNING in keeping with other failure messages.
ticket: 8270
(cherry picked from commit 6cfa5c113e981f14f70ccafa20abfa5c46b665ba)
---
src/clients/ksu/main.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c
index c6321c01b..35ff8978f 100644
--- a/src/clients/ksu/main.c
+++ b/src/clients/ksu/main.c
@@ -417,6 +417,16 @@ main (argc, argv)
if (hp){
if (gb_err) fprintf(stderr, "%s", gb_err);
fprintf(stderr, _("account %s: authorization failed\n"), target_user);
+
+ if (cmd != NULL) {
+ syslog(LOG_WARNING,
+ "Account %s: authorization for %s for execution of %s failed",
+ target_user, source_user, cmd);
+ } else {
+ syslog(LOG_WARNING, "Account %s: authorization of %s failed",
+ target_user, source_user);
+ }
+
exit(1);
}

View File

@ -0,0 +1,28 @@
From b96983de501f185a06e8b3d2909ef71033bd9e48 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 30 Mar 2021 14:35:28 +0200
Subject: [PATCH] Make KCM iteration fallback work with sssd-kcm
sssd-kcm returns KRB5_CC_IO if the operation code is not known.
ticket: 8990
(cherry picked from commit 06afae820a44c1dc96ad88a0b16c3e50bc938b2a)
(cherry picked from commit 2dbca7e14c945d6394e0e05f285a068dcd541295)
(cherry picked from commit f7702c5b11bdd186d03fed32568c9a252d049d44)
---
src/lib/krb5/ccache/cc_kcm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c
index 4141140c3..dae622feb 100644
--- a/src/lib/krb5/ccache/cc_kcm.c
+++ b/src/lib/krb5/ccache/cc_kcm.c
@@ -876,7 +876,7 @@ kcm_start_seq_get(krb5_context context, krb5_ccache cache,
ret = kcmreq_get_cred_list(&req, &creds);
if (ret)
goto cleanup;
- } else if (ret == KRB5_FCC_INTERNAL) {
+ } else if (ret == KRB5_FCC_INTERNAL || ret == KRB5_CC_IO) {
/* Fall back to GET_CRED_UUID_LIST. */
kcmreq_free(&req);
kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache);

View File

@ -1,36 +0,0 @@
From 16c745b7e9e239535a8c71dc7022b477a5165e01 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Wed, 13 Jun 2018 15:07:48 -0400
Subject: [PATCH] Make docs build python3-compatible
python3 removed execfile(), which we use for loading version data and
paths information in docs. Call exec() directly instead.
ticket: 8692 (new)
(cherry picked from commit a7c6d98480f1e33454173f88381921472d72f80a)
---
doc/conf.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/conf.py b/doc/conf.py
index 25ba214a8..0555808e6 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -50,7 +50,7 @@ copyright = u'1985-2018, MIT'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
-execfile("version.py")
+exec(open("version.py").read())
# The short X.Y version.
r_list = [r_major, r_minor]
if r_patch:
@@ -238,7 +238,7 @@ if 'mansubs' in tags:
ckeytab = '``@CKTNAME@``'
elif 'pathsubs' in tags:
# Read configured paths from a file produced by the build system.
- execfile('paths.py')
+ exec(open("paths.py").read())
else:
bindir = ':ref:`BINDIR <paths>`'
sbindir = ':ref:`SBINDIR <paths>`'

View File

@ -0,0 +1,365 @@
From 5d541f1f0b468b1c976acf8ec2359bd0c8c73be7 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Wed, 19 Jan 2022 19:46:08 +0100
Subject: [PATCH] Make kprop work for dump files larger than 4GB
If the dump file size does not fit in 32 bits, encode four zero bytes
(forcing an error for unmodified kpropd) followed by the size in the
next 64 bits.
Add a functional test case, but only run it when an environment
variable is set, as processing a 4GB dump file is too
resource-intensive for make check.
[ghudson@mit.edu: edited comments and commit message; eliminated use
of defined constant in some cases; added test case]
ticket: 9053 (new)
---
src/kprop/kprop.c | 37 +++++++++++++++++++++----------------
src/kprop/kprop.h | 12 ++++++++++++
src/kprop/kprop_util.c | 42 ++++++++++++++++++++++++++++++++++++++++++
src/kprop/kpropd.c | 33 +++++++++++++++++++++------------
src/tests/t_kprop.py | 34 ++++++++++++++++++++++++++++++++++
5 files changed, 130 insertions(+), 28 deletions(-)
diff --git a/src/kprop/kprop.c b/src/kprop/kprop.c
index 0b53aae7e..5adb4d31f 100644
--- a/src/kprop/kprop.c
+++ b/src/kprop/kprop.c
@@ -25,6 +25,7 @@
*/
#include "k5-int.h"
+#include <inttypes.h>
#include <locale.h>
#include <sys/file.h>
#include <signal.h>
@@ -71,11 +72,11 @@ static void open_connection(krb5_context context, char *host, int *fd_out);
static void kerberos_authenticate(krb5_context context,
krb5_auth_context *auth_context, int fd,
krb5_principal me, krb5_creds **new_creds);
-static int open_database(krb5_context context, char *data_fn, int *size);
+static int open_database(krb5_context context, char *data_fn, off_t *size);
static void close_database(krb5_context context, int fd);
static void xmit_database(krb5_context context,
krb5_auth_context auth_context, krb5_creds *my_creds,
- int fd, int database_fd, int in_database_size);
+ int fd, int database_fd, off_t in_database_size);
static void send_error(krb5_context context, krb5_creds *my_creds, int fd,
char *err_text, krb5_error_code err_code);
static void update_last_prop_file(char *hostname, char *file_name);
@@ -90,7 +91,8 @@ static void usage()
int
main(int argc, char **argv)
{
- int fd, database_fd, database_size;
+ int fd, database_fd;
+ off_t database_size;
krb5_error_code retval;
krb5_context context;
krb5_creds *my_creds;
@@ -339,7 +341,7 @@ kerberos_authenticate(krb5_context context, krb5_auth_context *auth_context,
* in the size of the database file.
*/
static int
-open_database(krb5_context context, char *data_fn, int *size)
+open_database(krb5_context context, char *data_fn, off_t *size)
{
struct stat stbuf, stbuf_ok;
char *data_ok_fn;
@@ -413,19 +415,18 @@ close_database(krb5_context context, int fd)
static void
xmit_database(krb5_context context, krb5_auth_context auth_context,
krb5_creds *my_creds, int fd, int database_fd,
- int in_database_size)
+ off_t in_database_size)
{
krb5_int32 n;
krb5_data inbuf, outbuf;
- char buf[KPROP_BUFSIZ];
+ char buf[KPROP_BUFSIZ], dbsize_buf[KPROP_DBSIZE_MAX_BUFSIZ];
krb5_error_code retval;
krb5_error *error;
- krb5_ui_4 database_size = in_database_size, send_size, sent_size;
+ uint64_t database_size = in_database_size, send_size, sent_size;
/* Send over the size. */
- send_size = htonl(database_size);
- inbuf.data = (char *)&send_size;
- inbuf.length = sizeof(send_size); /* must be 4, really */
+ inbuf = make_data(dbsize_buf, sizeof(dbsize_buf));
+ encode_database_size(database_size, &inbuf);
/* KPROP_CKSUMTYPE */
retval = krb5_mk_safe(context, auth_context, &inbuf, &outbuf, NULL);
if (retval) {
@@ -460,7 +461,7 @@ xmit_database(krb5_context context, krb5_auth_context auth_context,
retval = krb5_mk_priv(context, auth_context, &inbuf, &outbuf, NULL);
if (retval) {
snprintf(buf, sizeof(buf),
- "while encoding database block starting at %d",
+ "while encoding database block starting at %"PRIu64,
sent_size);
com_err(progname, retval, "%s", buf);
send_error(context, my_creds, fd, buf, retval);
@@ -471,14 +472,14 @@ xmit_database(krb5_context context, krb5_auth_context auth_context,
if (retval) {
krb5_free_data_contents(context, &outbuf);
com_err(progname, retval,
- _("while sending database block starting at %d"),
+ _("while sending database block starting at %"PRIu64),
sent_size);
exit(1);
}
krb5_free_data_contents(context, &outbuf);
sent_size += n;
if (debug)
- printf("%d bytes sent.\n", sent_size);
+ printf("%"PRIu64" bytes sent.\n", sent_size);
}
if (sent_size != database_size) {
com_err(progname, 0, _("Premature EOF found for database file!"));
@@ -533,10 +534,14 @@ xmit_database(krb5_context context, krb5_auth_context auth_context,
exit(1);
}
- memcpy(&send_size, outbuf.data, sizeof(send_size));
- send_size = ntohl(send_size);
+ retval = decode_database_size(&outbuf, &send_size);
+ if (retval) {
+ com_err(progname, retval, _("malformed sent database size message"));
+ exit(1);
+ }
if (send_size != database_size) {
- com_err(progname, 0, _("Kpropd sent database size %d, expecting %d"),
+ com_err(progname, 0, _("Kpropd sent database size %"PRIu64
+ ", expecting %"PRIu64),
send_size, database_size);
exit(1);
}
diff --git a/src/kprop/kprop.h b/src/kprop/kprop.h
index 75331cc8a..3a319b535 100644
--- a/src/kprop/kprop.h
+++ b/src/kprop/kprop.h
@@ -32,6 +32,7 @@
#define KPROP_PROT_VERSION "kprop5_01"
#define KPROP_BUFSIZ 32768
+#define KPROP_DBSIZE_MAX_BUFSIZ 12 /* max length of an encoded DB size */
/* pathnames are in osconf.h, included via k5-int.h */
@@ -41,3 +42,14 @@ int sockaddr2krbaddr(krb5_context context, int family, struct sockaddr *sa,
krb5_error_code
sn2princ_realm(krb5_context context, const char *hostname, const char *sname,
const char *realm, krb5_principal *princ_out);
+
+/*
+ * Encode size in four bytes (for backward compatibility) if it fits; otherwise
+ * use the larger encoding. buf must be allocated with at least
+ * KPROP_DBSIZE_MAX_BUFSIZ bytes.
+ */
+void encode_database_size(uint64_t size, krb5_data *buf);
+
+/* Decode a database size. Return KRB5KRB_ERR_GENERIC if buf has an invalid
+ * length or did not encode a 32-bit size compactly. */
+krb5_error_code decode_database_size(const krb5_data *buf, uint64_t *size_out);
diff --git a/src/kprop/kprop_util.c b/src/kprop/kprop_util.c
index c32d174b9..9d6b25389 100644
--- a/src/kprop/kprop_util.c
+++ b/src/kprop/kprop_util.c
@@ -96,3 +96,45 @@ sn2princ_realm(krb5_context context, const char *hostname, const char *sname,
(*princ_out)->type = KRB5_NT_SRV_HST;
return ret;
}
+
+void
+encode_database_size(uint64_t size, krb5_data *buf)
+{
+ assert(buf->length >= 12);
+ if (size > 0 && size <= UINT32_MAX) {
+ /* Encode in 32 bits for backward compatibility. */
+ store_32_be(size, buf->data);
+ buf->length = 4;
+ } else {
+ /* Set the first 32 bits to 0 and encode in the following 64 bits. */
+ store_32_be(0, buf->data);
+ store_64_be(size, buf->data + 4);
+ buf->length = 12;
+ }
+}
+
+krb5_error_code
+decode_database_size(const krb5_data *buf, uint64_t *size_out)
+{
+ uint64_t size;
+
+ if (buf->length == 12) {
+ /* A 12-byte buffer must have the first four bytes zeroed. */
+ if (load_32_be(buf->data) != 0)
+ return KRB5KRB_ERR_GENERIC;
+
+ /* The size is stored in the next 64 bits. Values from 1..2^32-1 must
+ * be encoded in four bytes. */
+ size = load_64_be(buf->data + 4);
+ if (size > 0 && size <= UINT32_MAX)
+ return KRB5KRB_ERR_GENERIC;
+ } else if (buf->length == 4) {
+ size = load_32_be(buf->data);
+ } else {
+ /* Invalid buffer size. */
+ return KRB5KRB_ERR_GENERIC;
+ }
+
+ *size_out = size;
+ return 0;
+}
diff --git a/src/kprop/kpropd.c b/src/kprop/kpropd.c
index 356e3e0e6..a83a86866 100644
--- a/src/kprop/kpropd.c
+++ b/src/kprop/kpropd.c
@@ -55,6 +55,7 @@
#include "com_err.h"
#include "fake-addrinfo.h"
+#include <inttypes.h>
#include <locale.h>
#include <ctype.h>
#include <sys/file.h>
@@ -1354,9 +1355,10 @@ static void
recv_database(krb5_context context, int fd, int database_fd,
krb5_data *confmsg)
{
- krb5_ui_4 database_size, received_size;
+ uint64_t database_size, received_size;
int n;
char buf[1024];
+ char dbsize_buf[KPROP_DBSIZE_MAX_BUFSIZ];
krb5_data inbuf, outbuf;
krb5_error_code retval;
@@ -1378,10 +1380,17 @@ recv_database(krb5_context context, int fd, int database_fd,
_("while decoding database size from client"));
exit(1);
}
- memcpy(&database_size, outbuf.data, sizeof(database_size));
+
+ retval = decode_database_size(&outbuf, &database_size);
+ if (retval) {
+ send_error(context, fd, retval, "malformed database size message");
+ com_err(progname, retval,
+ _("malformed database size message from client"));
+ exit(1);
+ }
+
krb5_free_data_contents(context, &inbuf);
krb5_free_data_contents(context, &outbuf);
- database_size = ntohl(database_size);
/* Initialize the initial vector. */
retval = krb5_auth_con_initivector(context, auth_context);
@@ -1401,7 +1410,7 @@ recv_database(krb5_context context, int fd, int database_fd,
retval = krb5_read_message(context, &fd, &inbuf);
if (retval) {
snprintf(buf, sizeof(buf),
- "while reading database block starting at offset %d",
+ "while reading database block starting at offset %"PRIu64,
received_size);
com_err(progname, retval, "%s", buf);
send_error(context, fd, retval, buf);
@@ -1412,8 +1421,8 @@ recv_database(krb5_context context, int fd, int database_fd,
retval = krb5_rd_priv(context, auth_context, &inbuf, &outbuf, NULL);
if (retval) {
snprintf(buf, sizeof(buf),
- "while decoding database block starting at offset %d",
- received_size);
+ "while decoding database block starting at offset %"
+ PRIu64, received_size);
com_err(progname, retval, "%s", buf);
send_error(context, fd, retval, buf);
krb5_free_data_contents(context, &inbuf);
@@ -1424,13 +1433,13 @@ recv_database(krb5_context context, int fd, int database_fd,
krb5_free_data_contents(context, &outbuf);
if (n < 0) {
snprintf(buf, sizeof(buf),
- "while writing database block starting at offset %d",
+ "while writing database block starting at offset %"PRIu64,
received_size);
send_error(context, fd, errno, buf);
} else if ((unsigned int)n != outbuf.length) {
snprintf(buf, sizeof(buf),
"incomplete write while writing database block starting "
- "at \noffset %d (%d written, %d expected)",
+ "at \noffset %"PRIu64" (%d written, %d expected)",
received_size, n, outbuf.length);
send_error(context, fd, KRB5KRB_ERR_GENERIC, buf);
}
@@ -1440,7 +1449,8 @@ recv_database(krb5_context context, int fd, int database_fd,
/* OK, we've seen the entire file. Did we get too many bytes? */
if (received_size > database_size) {
snprintf(buf, sizeof(buf),
- "Received %d bytes, expected %d bytes for database file",
+ "Received %"PRIu64" bytes, expected %"PRIu64
+ " bytes for database file",
received_size, database_size);
send_error(context, fd, KRB5KRB_ERR_GENERIC, buf);
}
@@ -1450,9 +1460,8 @@ recv_database(krb5_context context, int fd, int database_fd,
/* Create message acknowledging number of bytes received, but
* don't send it until kdb5_util returns successfully. */
- database_size = htonl(database_size);
- inbuf.data = (char *)&database_size;
- inbuf.length = sizeof(database_size);
+ inbuf = make_data(dbsize_buf, sizeof(dbsize_buf));
+ encode_database_size(database_size, &inbuf);
retval = krb5_mk_safe(context,auth_context,&inbuf,confmsg,NULL);
if (retval) {
com_err(progname, retval, "while encoding # of receieved bytes");
diff --git a/src/tests/t_kprop.py b/src/tests/t_kprop.py
index c33e4fea2..f8ffd653a 100755
--- a/src/tests/t_kprop.py
+++ b/src/tests/t_kprop.py
@@ -87,5 +87,39 @@ realm.run([kdb5_util, 'dump', dumpfile])
realm.run([kprop, '-f', dumpfile, '-P', str(realm.kprop_port()), hostname])
check_output(kpropd)
realm.run([kadminl, 'listprincs'], replica3, expected_msg='wakawaka')
+stop_daemon(kpropd)
+
+# This test is too resource-intensive to be included in "make check"
+# by default, but it can be enabled in the environment to test the
+# propagation of databases large enough to require a 12-byte encoding
+# of the database size.
+if 'KPROP_LARGE_DB_TEST' in os.environ:
+ output('Generating >4GB dumpfile\n')
+ with open(dumpfile, 'w') as f:
+ f.write('kdb5_util load_dump version 6\n')
+ f.write('princ\t38\t15\t3\t1\t0\tK/M@KRBTEST.COM\t64\t86400\t0\t0\t0'
+ '\t0\t0\t0\t8\t2\t0100\t9\t8\t0100010000000000\t2\t28'
+ '\tb93e105164625f6372656174696f6e404b5242544553542e434f4d00'
+ '\t1\t1\t18\t62\t2000408c027c250e8cc3b81476414f2214d57c1ce'
+ '38891e29792e87258247c73547df4d5756266931dd6686b62270e6568'
+ '95a31ec66bfe913b4f15226227\t-1;\n')
+ for i in range(1, 20000000):
+ f.write('princ\t38\t21\t1\t1\t0\tp%08d@KRBTEST.COM' % i)
+ f.write('\t0\t86400\t0\t0\t0\t0\t0\t0\t2\t27'
+ '\td73e1051757365722f61646d696e404b5242544553542e434f4d00'
+ '\t1\t1\t17\t46'
+ '\t10009c8ab7b3f89ccf3ca3ad98352a461b7f4f1b0c49'
+ '5605117591d9ad52ba4da0adef7a902126973ed2bdc3ffbf\t-1;\n')
+ assert os.path.getsize(dumpfile) > 4 * 1024 * 1024 * 1024
+ with open(dumpfile + '.dump_ok', 'w') as f:
+ f.write('\0')
+ conf_large = {'dbmodules': {'db': {'database_name': '$testdir/db.large'}},
+ 'realms': {'$realm': {'iprop_resync_timeout': '3600'}}}
+ large = realm.special_env('large', True, kdc_conf=conf_large)
+ kpropd = realm.start_kpropd(large, ['-d'])
+ realm.run([kprop, '-f', dumpfile, '-P', str(realm.kprop_port()), hostname])
+ check_output(kpropd)
+ realm.run([kadminl, 'getprinc', 'p19999999'], env=large,
+ expected_msg='Principal: p19999999')
success('kprop tests')
--
2.35.1

View File

@ -1,67 +0,0 @@
From 5587c1de938324faa1871e08ccfc835415acb443 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 17 Jul 2018 11:29:19 -0400
Subject: [PATCH] Make krb5kdc -p affect TCP ports
Now that the KDC listens for TCP connections by default (ticket 6731),
the "-p" option should affect both UDP and TCP default listening
ports.
ticket: 8715 (new)
(cherry picked from commit eb514587acc5c357bf0f554199bf0489b5515f8b)
---
doc/admin/admin_commands/krb5kdc.rst | 12 ++++++------
src/kdc/main.c | 12 ++++--------
2 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/doc/admin/admin_commands/krb5kdc.rst b/doc/admin/admin_commands/krb5kdc.rst
index 7ec4ee4d3..bda2c015c 100644
--- a/doc/admin/admin_commands/krb5kdc.rst
+++ b/doc/admin/admin_commands/krb5kdc.rst
@@ -57,12 +57,12 @@ The **-P** *pid_file* option tells the KDC to write its PID into
the KDC is still running and to allow init scripts to stop the correct
process.
-The **-p** *portnum* option specifies the default UDP port numbers
-which the KDC should listen on for Kerberos version 5 requests, as a
-comma-separated list. This value overrides the UDP port numbers
-specified in the :ref:`kdcdefaults` section of :ref:`kdc.conf(5)`, but
-may be overridden by realm-specific values. If no value is given from
-any source, the default port is 88.
+The **-p** *portnum* option specifies the default UDP and TCP port
+numbers which the KDC should listen on for Kerberos version 5
+requests, as a comma-separated list. This value overrides the port
+numbers specified in the :ref:`kdcdefaults` section of
+:ref:`kdc.conf(5)`, but may be overridden by realm-specific values.
+If no value is given from any source, the default port is 88.
The **-w** *numworkers* option tells the KDC to fork *numworkers*
processes to listen to the KDC ports and process requests in parallel.
diff --git a/src/kdc/main.c b/src/kdc/main.c
index ccac3a759..89dac23ae 100644
--- a/src/kdc/main.c
+++ b/src/kdc/main.c
@@ -793,19 +793,15 @@ initialize_realms(krb5_context kcontext, int argc, char **argv,
pid_file = optarg;
break;
case 'p':
- if (def_udp_listen)
- free(def_udp_listen);
+ free(def_udp_listen);
+ free(def_tcp_listen);
def_udp_listen = strdup(optarg);
- if (!def_udp_listen) {
+ def_tcp_listen = strdup(optarg);
+ if (def_udp_listen == NULL || def_tcp_listen == NULL) {
fprintf(stderr, _(" KDC cannot initialize. Not enough "
"memory\n"));
exit(1);
}
-#if 0 /* not yet */
- if (default_tcp_ports)
- free(default_tcp_ports);
- default_tcp_ports = strdup(optarg);
-#endif
break;
case 'T':
time_offset = atoi(optarg);

View File

@ -1,151 +0,0 @@
From ee941a490268bb045ec7e153bdf229adcd6d2f73 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 26 Mar 2018 10:54:29 -0400
Subject: [PATCH] Move zap() definition to k5-platform.h
Make it possible to use zap() in parts of the code which should not
include k5-int.h by moving its definition to k5-platform.h.
(cherry picked from commit df6bef6f9ea6a5f6f3956a2988cd658c78aae817)
---
src/include/k5-int.h | 45 -------------------------------------
src/include/k5-platform.h | 47 ++++++++++++++++++++++++++++++++++++++-
src/util/support/zap.c | 4 ++--
3 files changed, 48 insertions(+), 48 deletions(-)
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 1c1d9783b..69b81a7f7 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -639,51 +639,6 @@ krb5int_arcfour_gsscrypt(const krb5_keyblock *keyblock, krb5_keyusage usage,
krb5_error_code
k5_sha256(const krb5_data *in, size_t n, uint8_t out[K5_SHA256_HASHLEN]);
-/*
- * Attempt to zero memory in a way that compilers won't optimize out.
- *
- * This mechanism should work even for heap storage about to be freed,
- * or automatic storage right before we return from a function.
- *
- * Then, even if we leak uninitialized memory someplace, or UNIX
- * "core" files get created with world-read access, some of the most
- * sensitive data in the process memory will already be safely wiped.
- *
- * We're not going so far -- yet -- as to try to protect key data that
- * may have been written into swap space....
- */
-#ifdef _WIN32
-# define zap(ptr, len) SecureZeroMemory(ptr, len)
-#elif defined(__STDC_LIB_EXT1__)
-/*
- * Use memset_s() which cannot be optimized out. Avoid memset_s(NULL, 0, 0, 0)
- * which would cause a runtime constraint violation.
- */
-static inline void zap(void *ptr, size_t len)
-{
- if (len > 0)
- memset_s(ptr, len, 0, len);
-}
-#elif defined(__GNUC__) || defined(__clang__)
-/*
- * Use an asm statement which declares a memory clobber to force the memset to
- * be carried out. Avoid memset(NULL, 0, 0) which has undefined behavior.
- */
-static inline void zap(void *ptr, size_t len)
-{
- if (len > 0)
- memset(ptr, 0, len);
- __asm__ __volatile__("" : : "r" (ptr) : "memory");
-}
-#else
-/*
- * Use a function from libkrb5support to defeat inlining unless link-time
- * optimization is used. The function uses a volatile pointer, which prevents
- * current compilers from optimizing out the memset.
- */
-# define zap(ptr, len) krb5int_zap(ptr, len)
-#endif
-
/* Convenience function: zap and free ptr if it is non-NULL. */
static inline void
zapfree(void *ptr, size_t len)
diff --git a/src/include/k5-platform.h b/src/include/k5-platform.h
index 548c0486d..07ef6a4ca 100644
--- a/src/include/k5-platform.h
+++ b/src/include/k5-platform.h
@@ -40,7 +40,7 @@
* + [v]asprintf
* + strerror_r
* + mkstemp
- * + zap (support function; macro is in k5-int.h)
+ * + zap (support function and macro)
* + constant time memory comparison
* + path manipulation
* + _, N_, dgettext, bindtextdomain (for localization)
@@ -1022,6 +1022,51 @@ extern int krb5int_gettimeofday(struct timeval *tp, void *ignore);
#define gettimeofday krb5int_gettimeofday
#endif
+/*
+ * Attempt to zero memory in a way that compilers won't optimize out.
+ *
+ * This mechanism should work even for heap storage about to be freed,
+ * or automatic storage right before we return from a function.
+ *
+ * Then, even if we leak uninitialized memory someplace, or UNIX
+ * "core" files get created with world-read access, some of the most
+ * sensitive data in the process memory will already be safely wiped.
+ *
+ * We're not going so far -- yet -- as to try to protect key data that
+ * may have been written into swap space....
+ */
+#ifdef _WIN32
+# define zap(ptr, len) SecureZeroMemory(ptr, len)
+#elif defined(__STDC_LIB_EXT1__)
+/*
+ * Use memset_s() which cannot be optimized out. Avoid memset_s(NULL, 0, 0, 0)
+ * which would cause a runtime constraint violation.
+ */
+static inline void zap(void *ptr, size_t len)
+{
+ if (len > 0)
+ memset_s(ptr, len, 0, len);
+}
+#elif defined(__GNUC__) || defined(__clang__)
+/*
+ * Use an asm statement which declares a memory clobber to force the memset to
+ * be carried out. Avoid memset(NULL, 0, 0) which has undefined behavior.
+ */
+static inline void zap(void *ptr, size_t len)
+{
+ if (len > 0)
+ memset(ptr, 0, len);
+ __asm__ __volatile__("" : : "r" (ptr) : "memory");
+}
+#else
+/*
+ * Use a function from libkrb5support to defeat inlining unless link-time
+ * optimization is used. The function uses a volatile pointer, which prevents
+ * current compilers from optimizing out the memset.
+ */
+# define zap(ptr, len) krb5int_zap(ptr, len)
+#endif
+
extern void krb5int_zap(void *ptr, size_t len);
/*
diff --git a/src/util/support/zap.c b/src/util/support/zap.c
index ed31630db..2f6cdd70e 100644
--- a/src/util/support/zap.c
+++ b/src/util/support/zap.c
@@ -25,8 +25,8 @@
*/
/*
- * krb5int_zap() is used by zap() (a static inline function defined in
- * k5-int.h) on non-Windows, non-gcc compilers, in order to prevent the
+ * krb5int_zap() is used by zap() (a macro or static inline function defined in
+ * k5-platform.h) on non-Windows, non-gcc compilers, in order to prevent the
* compiler from inlining and optimizing out the memset() call.
*/

View File

@ -0,0 +1,49 @@
From f7b6d43533d1d9ec3960e3d7f375995896768aef Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 6 May 2020 16:03:13 -0400
Subject: [PATCH] Omit KDC indicator check for S4U2Self requests
As there was no initial ticket exchange from the client for an
S4U2Self request, the auth indicator check is inapplicable (and would
always fail if any auth indicators are required).
ticket: 8902 (new)
(cherry picked from commit 183631fbf72351c2d5fc7d60b2d9fc4d09fe7465)
(cherry picked from commit 442f1fa5b2e4034954a51048414cc0863b914379)
---
src/kdc/do_tgs_req.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index 241f34e2a..463a9c0dd 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -392,8 +392,8 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
}
authtime = subject_tkt->times.authtime;
- /* Extract auth indicators from the subject ticket, except for S4U2Self
- * requests (where the client didn't authenticate). */
+ /* Extract and check auth indicators from the subject ticket, except for
+ * S4U2Self requests (where the client didn't authenticate). */
if (s4u_x509_user == NULL) {
errcode = get_auth_indicators(kdc_context, subject_tkt, local_tgt,
&local_tgt_key, &auth_indicators);
@@ -401,12 +401,12 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
status = "GET_AUTH_INDICATORS";
goto cleanup;
}
- }
- errcode = check_indicators(kdc_context, server, auth_indicators);
- if (errcode) {
- status = "HIGHER_AUTHENTICATION_REQUIRED";
- goto cleanup;
+ errcode = check_indicators(kdc_context, server, auth_indicators);
+ if (errcode) {
+ status = "HIGHER_AUTHENTICATION_REQUIRED";
+ goto cleanup;
+ }
}
if (is_referral)

View File

@ -0,0 +1,35 @@
From e1b2c967266b14bc37e5ed11e6c0525bd259e0bb Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Sat, 6 Jun 2020 11:03:37 +0200
Subject: [PATCH] Omit PA_FOR_USER if we can't compute its checksum
OpenSSL in FIPS mode will refuse to perform hmac-md5. Omit the legacy
PA_FOR_USER element in this case rather than failing out.
[ghudson@mit.edu: minor code and comment edits; wrote commit message]
ticket: 8912 (new)
(cherry picked from commit 03f122bdb22cfa53c7d855ed929c9541e56365e0)
(cherry picked from commit 086de78292b8ae89aba8a72926831124da44205d)
---
src/lib/krb5/krb/s4u_creds.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
index fc5c886d6..d8f486dc6 100644
--- a/src/lib/krb5/krb/s4u_creds.c
+++ b/src/lib/krb5/krb/s4u_creds.c
@@ -534,6 +534,13 @@ krb5_get_self_cred_from_kdc(krb5_context context,
if (s4u_user.user_id.user != NULL && s4u_user.user_id.user->length) {
code = build_pa_for_user(context, tgtptr, &s4u_user.user_id,
&in_padata[1]);
+ /*
+ * If we couldn't compute the hmac-md5 checksum, send only the
+ * KRB5_PADATA_S4U_X509_USER; this will still work against modern
+ * Windows and MIT KDCs.
+ */
+ if (code == KRB5_CRYPTO_INTERNAL)
+ code = 0;
if (code != 0) {
krb5_free_pa_data(context, in_padata);
goto cleanup;

View File

@ -0,0 +1,257 @@
From 6265b0fbc59e13756364b97a5e3e8672514f8302 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Tue, 28 Apr 2020 18:15:55 +0200
Subject: [PATCH] Pass channel bindings through SPNEGO
ticket: 8907 (new)
(cherry picked from commit d16325a24c34ec9a5f6fb4910987f162e0d4d9cd)
(cherry picked from commit ee79bd43005245d3e5a2d3ec6d61146945e77717)
---
src/lib/gssapi/spnego/gssapiP_negoex.h | 8 ++---
src/lib/gssapi/spnego/negoex_ctx.c | 34 +++++++++++----------
src/lib/gssapi/spnego/spnego_mech.c | 41 +++++++++++++-------------
3 files changed, 43 insertions(+), 40 deletions(-)
diff --git a/src/lib/gssapi/spnego/gssapiP_negoex.h b/src/lib/gssapi/spnego/gssapiP_negoex.h
index 44b08f523..489ab7c42 100644
--- a/src/lib/gssapi/spnego/gssapiP_negoex.h
+++ b/src/lib/gssapi/spnego/gssapiP_negoex.h
@@ -201,10 +201,10 @@ negoex_restrict_auth_schemes(spnego_gss_ctx_id_t ctx,
OM_uint32
negoex_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
gss_name_t target_name, OM_uint32 req_flags, OM_uint32 time_req,
- gss_buffer_t input_token, gss_buffer_t output_token,
- OM_uint32 *time_rec);
+ gss_buffer_t input_token, gss_channel_bindings_t bindings,
+ gss_buffer_t output_token, OM_uint32 *time_rec);
OM_uint32
negoex_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
- gss_buffer_t input_token, gss_buffer_t output_token,
- OM_uint32 *time_rec);
+ gss_buffer_t input_token, gss_channel_bindings_t bindings,
+ gss_buffer_t output_token, OM_uint32 *time_rec);
diff --git a/src/lib/gssapi/spnego/negoex_ctx.c b/src/lib/gssapi/spnego/negoex_ctx.c
index 18d9d4147..8848ee4db 100644
--- a/src/lib/gssapi/spnego/negoex_ctx.c
+++ b/src/lib/gssapi/spnego/negoex_ctx.c
@@ -276,7 +276,8 @@ static OM_uint32
mech_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
gss_name_t target, OM_uint32 req_flags, OM_uint32 time_req,
struct negoex_message *messages, size_t nmessages,
- gss_buffer_t output_token, OM_uint32 *time_rec)
+ gss_channel_bindings_t bindings, gss_buffer_t output_token,
+ OM_uint32 *time_rec)
{
OM_uint32 major, first_major = 0, first_minor = 0;
struct negoex_auth_mech *mech = NULL;
@@ -316,10 +317,9 @@ mech_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
major = gss_init_sec_context(minor, cred, &mech->mech_context, target,
- mech->oid, req_flags, time_req,
- GSS_C_NO_CHANNEL_BINDINGS, input_token,
- &ctx->actual_mech, output_token,
- &ctx->ctx_flags, time_rec);
+ mech->oid, req_flags, time_req, bindings,
+ input_token, &ctx->actual_mech,
+ output_token, &ctx->ctx_flags, time_rec);
if (major == GSS_S_COMPLETE)
mech->complete = 1;
@@ -351,7 +351,8 @@ mech_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
static OM_uint32
mech_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
gss_cred_id_t cred, struct negoex_message *messages,
- size_t nmessages, gss_buffer_t output_token, OM_uint32 *time_rec)
+ size_t nmessages, gss_channel_bindings_t bindings,
+ gss_buffer_t output_token, OM_uint32 *time_rec)
{
OM_uint32 major, tmpmin;
struct negoex_auth_mech *mech;
@@ -395,10 +396,10 @@ mech_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
gss_release_cred(&tmpmin, &ctx->deleg_cred);
major = gss_accept_sec_context(minor, &mech->mech_context, cred,
- &msg->token, GSS_C_NO_CHANNEL_BINDINGS,
- &ctx->internal_name, &ctx->actual_mech,
- output_token, &ctx->ctx_flags,
- time_rec, &ctx->deleg_cred);
+ &msg->token, bindings, &ctx->internal_name,
+ &ctx->actual_mech, output_token,
+ &ctx->ctx_flags, time_rec,
+ &ctx->deleg_cred);
if (major == GSS_S_COMPLETE)
mech->complete = 1;
@@ -609,8 +610,8 @@ make_output_token(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
OM_uint32
negoex_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
gss_name_t target_name, OM_uint32 req_flags, OM_uint32 time_req,
- gss_buffer_t input_token, gss_buffer_t output_token,
- OM_uint32 *time_rec)
+ gss_buffer_t input_token, gss_channel_bindings_t bindings,
+ gss_buffer_t output_token, OM_uint32 *time_rec)
{
OM_uint32 major, tmpmin;
gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
@@ -663,7 +664,8 @@ negoex_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
/* Process the input token and/or produce an output token. This may prune
* the mech list, but on success there will be at least one mech entry. */
major = mech_init(minor, ctx, cred, target_name, req_flags, time_req,
- messages, nmessages, &mech_output_token, time_rec);
+ messages, nmessages, bindings, &mech_output_token,
+ time_rec);
if (major != GSS_S_COMPLETE)
goto cleanup;
assert(!K5_TAILQ_EMPTY(&ctx->negoex_mechs));
@@ -701,8 +703,8 @@ cleanup:
OM_uint32
negoex_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
- gss_buffer_t input_token, gss_buffer_t output_token,
- OM_uint32 *time_rec)
+ gss_buffer_t input_token, gss_channel_bindings_t bindings,
+ gss_buffer_t output_token, OM_uint32 *time_rec)
{
OM_uint32 major, tmpmin;
gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
@@ -754,7 +756,7 @@ negoex_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
* prune the list to a single mech. Continue on error if an output token
* is generated, so that we send the token to the initiator.
*/
- major = mech_accept(minor, ctx, cred, messages, nmessages,
+ major = mech_accept(minor, ctx, cred, messages, nmessages, bindings,
&mech_output_token, time_rec);
if (major != GSS_S_COMPLETE && mech_output_token.length == 0)
goto cleanup;
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
index 594fc5894..4cf011143 100644
--- a/src/lib/gssapi/spnego/spnego_mech.c
+++ b/src/lib/gssapi/spnego/spnego_mech.c
@@ -130,6 +130,7 @@ init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
OM_uint32, gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
+ gss_channel_bindings_t,
gss_buffer_t, OM_uint32 *, send_token_flag *);
static OM_uint32
@@ -144,8 +145,8 @@ acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
OM_uint32 *, send_token_flag *);
static OM_uint32
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
- gss_buffer_t, gss_buffer_t, OM_uint32 *, OM_uint32 *,
- send_token_flag *);
+ gss_buffer_t, gss_channel_bindings_t, gss_buffer_t,
+ OM_uint32 *, OM_uint32 *, send_token_flag *);
static gss_OID
negotiate_mech(spnego_gss_ctx_id_t, gss_OID_set, OM_uint32 *);
@@ -905,6 +906,7 @@ init_ctx_call_init(OM_uint32 *minor_status,
OM_uint32 req_flags,
OM_uint32 time_req,
gss_buffer_t mechtok_in,
+ gss_channel_bindings_t bindings,
gss_buffer_t mechtok_out,
OM_uint32 *time_rec,
send_token_flag *send_token)
@@ -921,15 +923,14 @@ init_ctx_call_init(OM_uint32 *minor_status,
if (gss_oid_equal(sc->internal_mech, &negoex_mech)) {
ret = negoex_init(minor_status, sc, mcred, target_name,
mech_req_flags, time_req, mechtok_in,
- mechtok_out, time_rec);
+ bindings, mechtok_out, time_rec);
} else {
ret = gss_init_sec_context(minor_status, mcred,
&sc->ctx_handle, target_name,
sc->internal_mech, mech_req_flags,
- time_req, GSS_C_NO_CHANNEL_BINDINGS,
- mechtok_in, &sc->actual_mech,
- mechtok_out, &sc->ctx_flags,
- time_rec);
+ time_req, bindings, mechtok_in,
+ &sc->actual_mech, mechtok_out,
+ &sc->ctx_flags, time_rec);
}
/* Bail out if the acceptor gave us an error token but the mech didn't
@@ -981,8 +982,8 @@ init_ctx_call_init(OM_uint32 *minor_status,
gss_delete_sec_context(&tmpmin, &sc->ctx_handle, GSS_C_NO_BUFFER);
tmpret = init_ctx_call_init(&tmpmin, sc, spcred, acc_negState,
target_name, req_flags, time_req,
- mechtok_in, mechtok_out, time_rec,
- send_token);
+ mechtok_in, bindings, mechtok_out,
+ time_rec, send_token);
if (HARD_ERROR(tmpret))
goto fail;
*minor_status = tmpmin;
@@ -1004,7 +1005,7 @@ spnego_gss_init_sec_context(
gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
- gss_channel_bindings_t input_chan_bindings,
+ gss_channel_bindings_t bindings,
gss_buffer_t input_token,
gss_OID *actual_mech,
gss_buffer_t output_token,
@@ -1084,8 +1085,8 @@ spnego_gss_init_sec_context(
if (!spnego_ctx->mech_complete) {
ret = init_ctx_call_init(minor_status, spnego_ctx, spcred,
acc_negState, target_name, req_flags,
- time_req, mechtok_in, &mechtok_out,
- time_rec, &send_token);
+ time_req, mechtok_in, bindings,
+ &mechtok_out, time_rec, &send_token);
if (ret != GSS_S_COMPLETE)
goto cleanup;
@@ -1542,8 +1543,9 @@ cleanup:
static OM_uint32
acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
- gss_buffer_t mechtok_out, OM_uint32 *time_rec,
- OM_uint32 *negState, send_token_flag *tokflag)
+ gss_channel_bindings_t bindings, gss_buffer_t mechtok_out,
+ OM_uint32 *time_rec, OM_uint32 *negState,
+ send_token_flag *tokflag)
{
OM_uint32 ret, tmpmin;
gss_OID_desc mechoid;
@@ -1568,13 +1570,12 @@ acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
if (negoex) {
ret = negoex_accept(minor_status, sc, mcred, mechtok_in,
- mechtok_out, time_rec);
+ bindings, mechtok_out, time_rec);
} else {
(void) gss_release_name(&tmpmin, &sc->internal_name);
(void) gss_release_cred(&tmpmin, &sc->deleg_cred);
ret = gss_accept_sec_context(minor_status, &sc->ctx_handle,
- mcred, mechtok_in,
- GSS_C_NO_CHANNEL_BINDINGS,
+ mcred, mechtok_in, bindings,
&sc->internal_name,
&sc->actual_mech, mechtok_out,
&sc->ctx_flags, time_rec,
@@ -1620,7 +1621,7 @@ spnego_gss_accept_sec_context(
gss_ctx_id_t *context_handle,
gss_cred_id_t verifier_cred_handle,
gss_buffer_t input_token,
- gss_channel_bindings_t input_chan_bindings,
+ gss_channel_bindings_t bindings,
gss_name_t *src_name,
gss_OID *mech_type,
gss_buffer_t output_token,
@@ -1734,8 +1735,8 @@ spnego_gss_accept_sec_context(
*/
if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
ret = acc_ctx_call_acc(minor_status, sc, spcred, mechtok_in,
- &mechtok_out, time_rec, &negState,
- &return_token);
+ bindings, &mechtok_out, time_rec,
+ &negState, &return_token);
}
/* Step 3: process or generate the MIC, if the negotiated mech is

View File

@ -0,0 +1,59 @@
From e57cdf6610f0b7c8ac38f9b2342b74b8c9e5bc54 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sun, 26 Apr 2020 19:55:54 -0400
Subject: [PATCH] Pass gss_localname() through SPNEGO
ticket: 8897 (new)
(cherry picked from commit f7b8a6432bd289bdc528017be122305f95b8e285)
(cherry picked from commit 646212314a580a8cdffdacda9cb3c8f806471b08)
---
src/lib/gssapi/spnego/gssapiP_spnego.h | 8 ++++++++
src/lib/gssapi/spnego/spnego_mech.c | 9 ++++++++-
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h
index a93763314..066ec736f 100644
--- a/src/lib/gssapi/spnego/gssapiP_spnego.h
+++ b/src/lib/gssapi/spnego/gssapiP_spnego.h
@@ -357,6 +357,14 @@ OM_uint32 KRB5_CALLCONV spnego_gss_wrap_size_limit
OM_uint32 *max_input_size
);
+OM_uint32 KRB5_CALLCONV spnego_gss_localname
+(
+ OM_uint32 *minor_status,
+ const gss_name_t pname,
+ const gss_const_OID mech_type,
+ gss_buffer_t localname
+);
+
OM_uint32 KRB5_CALLCONV spnego_gss_get_mic
(
OM_uint32 *minor_status,
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
index ec0bae6a4..594fc5894 100644
--- a/src/lib/gssapi/spnego/spnego_mech.c
+++ b/src/lib/gssapi/spnego/spnego_mech.c
@@ -237,7 +237,7 @@ static struct gss_config spnego_mechanism =
spnego_gss_inquire_context, /* gss_inquire_context */
NULL, /* gss_internal_release_oid */
spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */
- NULL, /* gssd_pname_to_uid */
+ spnego_gss_localname,
NULL, /* gss_userok */
NULL, /* gss_export_name */
spnego_gss_duplicate_name, /* gss_duplicate_name */
@@ -2371,6 +2371,13 @@ spnego_gss_wrap_size_limit(
return (ret);
}
+OM_uint32 KRB5_CALLCONV
+spnego_gss_localname(OM_uint32 *minor_status, const gss_name_t pname,
+ const gss_const_OID mech_type, gss_buffer_t localname)
+{
+ return gss_localname(minor_status, pname, GSS_C_NO_OID, localname);
+}
+
OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic(
OM_uint32 *minor_status,

View File

@ -0,0 +1,64 @@
From 105ba83436476f5a08759b8e97bfb0c5a69596b9 Mon Sep 17 00:00:00 2001
From: Zoltan Borbely <Zoltan.Borbely@morganstanley.com>
Date: Tue, 28 Jan 2025 16:39:25 -0500
Subject: [PATCH] Prevent overflow when calculating ulog block size
In kdb_log.c:resize(), log an error and fail if the update size is
larger than the largest possible block size (2^16-1).
CVE-2025-24528:
In MIT krb5 release 1.7 and later with incremental propagation
enabled, an authenticated attacker can cause kadmind to write beyond
the end of the mapped region for the iprop log file, likely causing a
process crash.
[ghudson@mit.edu: edited commit message and added CVE description]
ticket: 9159 (new)
tags: pullup
target_version: 1.21-next
(cherry picked from commit 78ceba024b64d49612375be4a12d1c066b0bfbd0)
---
src/lib/kdb/kdb_log.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/lib/kdb/kdb_log.c b/src/lib/kdb/kdb_log.c
index e9b95fce59..c805ebd988 100644
--- a/src/lib/kdb/kdb_log.c
+++ b/src/lib/kdb/kdb_log.c
@@ -183,7 +183,7 @@ extend_file_to(int fd, unsigned int new_size)
*/
static krb5_error_code
resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd,
- unsigned int recsize)
+ unsigned int recsize, const kdb_incr_update_t *upd)
{
unsigned int new_block, new_size;
@@ -195,6 +195,12 @@ resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd,
new_block *= ULOG_BLOCK;
new_size += ulogentries * new_block;
+ if (new_block > UINT16_MAX) {
+ syslog(LOG_ERR, _("ulog overflow caused by principal %.*s"),
+ upd->kdb_princ_name.utf8str_t_len,
+ upd->kdb_princ_name.utf8str_t_val);
+ return KRB5_LOG_ERROR;
+ }
if (new_size > MAXLOGLEN)
return KRB5_LOG_ERROR;
@@ -291,7 +297,7 @@ store_update(kdb_log_context *log_ctx, kdb_incr_update_t *upd)
recsize = sizeof(kdb_ent_header_t) + upd_size;
if (recsize > ulog->kdb_block) {
- retval = resize(ulog, ulogentries, log_ctx->ulogfd, recsize);
+ retval = resize(ulog, ulogentries, log_ctx->ulogfd, recsize, upd);
if (retval)
return retval;
}
--
2.48.1

View File

@ -1,114 +0,0 @@
From 5d868264bca1771aa16abbc8cc0aefb0e1750a73 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 6 Jun 2018 17:58:41 -0400
Subject: [PATCH] Process profile includedir in sorted order
In the profile library, use k5_dir_filenames() so that files within an
included directory are read in a predictable order (alphanumeric
within the C locale).
ticket: 8686
(cherry picked from commit f574eda48740ad192f51e9a382a205e2ea0e60ad)
---
doc/admin/conf_files/krb5_conf.rst | 4 ++-
src/util/profile/prof_parse.c | 56 +++++-------------------------
2 files changed, 12 insertions(+), 48 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 2574e5c26..ce545492d 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -60,7 +60,9 @@ alphanumeric characters, dashes, or underscores. Starting in release
1.15, files with names ending in ".conf" are also included, unless the
name begins with ".". Included profile files are syntactically
independent of their parents, so each included file must begin with a
-section header.
+section header. Starting in release 1.17, files are read in
+alphanumeric order; in previous releases, they may be read in any
+order.
The krb5.conf file can specify that configuration should be obtained
from a loadable module, rather than the file itself, using the
diff --git a/src/util/profile/prof_parse.c b/src/util/profile/prof_parse.c
index 1baceea9e..531e4a099 100644
--- a/src/util/profile/prof_parse.c
+++ b/src/util/profile/prof_parse.c
@@ -246,59 +246,22 @@ static int valid_name(const char *filename)
* Include files within dirname. Only files with names ending in ".conf", or
* consisting entirely of alphanumeric characters, dashes, and underscores are
* included. This restriction avoids including editor backup files, .rpmsave
- * files, and the like.
+ * files, and the like. Files are processed in alphanumeric order.
*/
static errcode_t parse_include_dir(const char *dirname,
struct profile_node *root_section)
{
-#ifdef _WIN32
- char *wildcard = NULL, *pathname;
- WIN32_FIND_DATA ffd;
- HANDLE handle;
errcode_t retval = 0;
+ char **fnames, *pathname;
+ int i;
- if (asprintf(&wildcard, "%s\\*", dirname) < 0)
- return ENOMEM;
-
- handle = FindFirstFile(wildcard, &ffd);
- if (handle == INVALID_HANDLE_VALUE) {
- retval = PROF_FAIL_INCLUDE_DIR;
- goto cleanup;
- }
-
- do {
- if (!valid_name(ffd.cFileName))
- continue;
- if (asprintf(&pathname, "%s\\%s", dirname, ffd.cFileName) < 0) {
- retval = ENOMEM;
- break;
- }
- retval = parse_include_file(pathname, root_section);
- free(pathname);
- if (retval)
- break;
- } while (FindNextFile(handle, &ffd) != 0);
-
- FindClose(handle);
-
-cleanup:
- free(wildcard);
- return retval;
-
-#else /* not _WIN32 */
-
- DIR *dir;
- char *pathname;
- errcode_t retval = 0;
- struct dirent *ent;
-
- dir = opendir(dirname);
- if (dir == NULL)
+ if (k5_dir_filenames(dirname, &fnames) != 0)
return PROF_FAIL_INCLUDE_DIR;
- while ((ent = readdir(dir)) != NULL) {
- if (!valid_name(ent->d_name))
+
+ for (i = 0; fnames != NULL && fnames[i] != NULL; i++) {
+ if (!valid_name(fnames[i]))
continue;
- if (asprintf(&pathname, "%s/%s", dirname, ent->d_name) < 0) {
+ if (asprintf(&pathname, "%s/%s", dirname, fnames[i]) < 0) {
retval = ENOMEM;
break;
}
@@ -307,9 +270,8 @@ cleanup:
if (retval)
break;
}
- closedir(dir);
+ k5_free_filenames(fnames);
return retval;
-#endif /* not _WIN32 */
}
static errcode_t parse_line(char *line, struct parse_state *state,

View File

@ -1,393 +0,0 @@
From 7c59b7ee063489a4259c34b725728fee7e411c46 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 21 Dec 2017 11:28:52 -0500
Subject: [PATCH] Refactor KDC krb5_pa_data utility functions
Move alloc_padata from fast_util.c to kdc_util.c and make it
non-static so it can be used by other files. Rename it to
alloc_pa_data for consistency with add_pa_data_element. Make it
correctly handle zero length using a null contents pointer.
Make add_pa_data_element claim both the container and contents memory
from the caller, now that callers can use alloc_pa_data to simplify
allocation and copying. Remove the copy parameter and the unused
context parameter, and put the list parameter first. Adjust all
callers accordingly, making small simplifications to memory handling
where applicable.
(cherry picked from commit 4af478c18b02e1d2444a328bb79e6976ef3d312b)
---
src/kdc/fast_util.c | 28 +------
src/kdc/kdc_preauth.c | 14 ++--
src/kdc/kdc_util.c | 187 +++++++++++++++++++++---------------------
src/kdc/kdc_util.h | 8 +-
4 files changed, 109 insertions(+), 128 deletions(-)
diff --git a/src/kdc/fast_util.c b/src/kdc/fast_util.c
index e05107ef3..6a3fc11b9 100644
--- a/src/kdc/fast_util.c
+++ b/src/kdc/fast_util.c
@@ -451,36 +451,12 @@ kdc_fast_hide_client(struct kdc_request_state *state)
return (state->fast_options & KRB5_FAST_OPTION_HIDE_CLIENT_NAMES) != 0;
}
-/* Allocate a pa-data entry with an uninitialized buffer of size len. */
-static krb5_error_code
-alloc_padata(krb5_preauthtype pa_type, size_t len, krb5_pa_data **out)
-{
- krb5_pa_data *pa;
- uint8_t *buf;
-
- *out = NULL;
- buf = malloc(len);
- if (buf == NULL)
- return ENOMEM;
- pa = malloc(sizeof(*pa));
- if (pa == NULL) {
- free(buf);
- return ENOMEM;
- }
- pa->magic = KV5M_PA_DATA;
- pa->pa_type = pa_type;
- pa->length = len;
- pa->contents = buf;
- *out = pa;
- return 0;
-}
-
/* Create a pa-data entry with the specified type and contents. */
static krb5_error_code
make_padata(krb5_preauthtype pa_type, const void *contents, size_t len,
krb5_pa_data **out)
{
- if (alloc_padata(pa_type, len, out) != 0)
+ if (alloc_pa_data(pa_type, len, out) != 0)
return ENOMEM;
memcpy((*out)->contents, contents, len);
return 0;
@@ -720,7 +696,7 @@ kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
goto cleanup;
/* Construct the cookie pa-data entry. */
- ret = alloc_padata(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, &pa);
+ ret = alloc_pa_data(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, &pa);
memcpy(pa->contents, "MIT1", 4);
store_32_be(kvno, pa->contents + 4);
memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length);
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 739c5e776..edc30bd83 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -1617,18 +1617,20 @@ return_referral_enc_padata( krb5_context context,
{
krb5_error_code code;
krb5_tl_data tl_data;
- krb5_pa_data pa_data;
+ krb5_pa_data *pa;
tl_data.tl_data_type = KRB5_TL_SVR_REFERRAL_DATA;
code = krb5_dbe_lookup_tl_data(context, server, &tl_data);
if (code || tl_data.tl_data_length == 0)
return 0;
- pa_data.magic = KV5M_PA_DATA;
- pa_data.pa_type = KRB5_PADATA_SVR_REFERRAL_INFO;
- pa_data.length = tl_data.tl_data_length;
- pa_data.contents = tl_data.tl_data_contents;
- return add_pa_data_element(context, &pa_data, &reply->enc_padata, TRUE);
+ code = alloc_pa_data(KRB5_PADATA_SVR_REFERRAL_INFO, tl_data.tl_data_length,
+ &pa);
+ if (code)
+ return code;
+ memcpy(pa->contents, tl_data.tl_data_contents, tl_data.tl_data_length);
+ /* add_pa_data_element() claims pa on success or failure. */
+ return add_pa_data_element(&reply->enc_padata, pa);
}
krb5_error_code
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 754570c01..13111215d 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -1353,9 +1353,9 @@ kdc_make_s4u2self_rep(krb5_context context,
krb5_enc_kdc_rep_part *reply_encpart)
{
krb5_error_code code;
- krb5_data *data = NULL;
+ krb5_data *der_user_id = NULL, *der_s4u_x509_user = NULL;
krb5_pa_s4u_x509_user rep_s4u_user;
- krb5_pa_data padata;
+ krb5_pa_data *pa;
krb5_enctype enctype;
krb5_keyusage usage;
@@ -1366,7 +1366,7 @@ kdc_make_s4u2self_rep(krb5_context context,
rep_s4u_user.user_id.options =
req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE;
- code = encode_krb5_s4u_userid(&rep_s4u_user.user_id, &data);
+ code = encode_krb5_s4u_userid(&rep_s4u_user.user_id, &der_user_id);
if (code != 0)
goto cleanup;
@@ -1377,29 +1377,25 @@ kdc_make_s4u2self_rep(krb5_context context,
code = krb5_c_make_checksum(context, req_s4u_user->cksum.checksum_type,
tgs_subkey != NULL ? tgs_subkey : tgs_session,
- usage, data,
- &rep_s4u_user.cksum);
+ usage, der_user_id, &rep_s4u_user.cksum);
if (code != 0)
goto cleanup;
- krb5_free_data(context, data);
- data = NULL;
-
- code = encode_krb5_pa_s4u_x509_user(&rep_s4u_user, &data);
+ code = encode_krb5_pa_s4u_x509_user(&rep_s4u_user, &der_s4u_x509_user);
if (code != 0)
goto cleanup;
- padata.magic = KV5M_PA_DATA;
- padata.pa_type = KRB5_PADATA_S4U_X509_USER;
- padata.length = data->length;
- padata.contents = (krb5_octet *)data->data;
-
- code = add_pa_data_element(context, &padata, &reply->padata, FALSE);
+ /* Add a padata element, stealing memory from der_s4u_x509_user. */
+ code = alloc_pa_data(KRB5_PADATA_S4U_X509_USER, 0, &pa);
+ if (code != 0)
+ goto cleanup;
+ pa->length = der_s4u_x509_user->length;
+ pa->contents = (uint8_t *)der_s4u_x509_user->data;
+ der_s4u_x509_user->data = NULL;
+ /* add_pa_data_element() claims pa on success or failure. */
+ code = add_pa_data_element(&reply->padata, pa);
if (code != 0)
goto cleanup;
-
- free(data);
- data = NULL;
if (tgs_subkey != NULL)
enctype = tgs_subkey->enctype;
@@ -1413,33 +1409,27 @@ kdc_make_s4u2self_rep(krb5_context context,
*/
if ((req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) &&
enctype_requires_etype_info_2(enctype) == FALSE) {
- padata.length = req_s4u_user->cksum.length +
- rep_s4u_user.cksum.length;
- padata.contents = malloc(padata.length);
- if (padata.contents == NULL) {
- code = ENOMEM;
+ code = alloc_pa_data(KRB5_PADATA_S4U_X509_USER,
+ req_s4u_user->cksum.length +
+ rep_s4u_user.cksum.length, &pa);
+ if (code != 0)
goto cleanup;
- }
+ memcpy(pa->contents,
+ req_s4u_user->cksum.contents, req_s4u_user->cksum.length);
+ memcpy(&pa->contents[req_s4u_user->cksum.length],
+ rep_s4u_user.cksum.contents, rep_s4u_user.cksum.length);
- memcpy(padata.contents,
- req_s4u_user->cksum.contents,
- req_s4u_user->cksum.length);
- memcpy(&padata.contents[req_s4u_user->cksum.length],
- rep_s4u_user.cksum.contents,
- rep_s4u_user.cksum.length);
-
- code = add_pa_data_element(context,&padata,
- &reply_encpart->enc_padata, FALSE);
- if (code != 0) {
- free(padata.contents);
+ /* add_pa_data_element() claims pa on success or failure. */
+ code = add_pa_data_element(&reply_encpart->enc_padata, pa);
+ if (code != 0)
goto cleanup;
- }
}
cleanup:
if (rep_s4u_user.cksum.contents != NULL)
krb5_free_checksum_contents(context, &rep_s4u_user.cksum);
- krb5_free_data(context, data);
+ krb5_free_data(context, der_user_id);
+ krb5_free_data(context, der_s4u_x509_user);
return code;
}
@@ -1707,46 +1697,50 @@ enctype_requires_etype_info_2(krb5_enctype enctype)
}
}
-/* XXX where are the generic helper routines for this? */
+/* Allocate a pa-data entry with an uninitialized buffer of size len. */
krb5_error_code
-add_pa_data_element(krb5_context context,
- krb5_pa_data *padata,
- krb5_pa_data ***inout_padata,
- krb5_boolean copy)
+alloc_pa_data(krb5_preauthtype pa_type, size_t len, krb5_pa_data **out)
{
- int i;
- krb5_pa_data **p;
+ krb5_pa_data *pa;
+ uint8_t *buf = NULL;
- if (*inout_padata != NULL) {
- for (i = 0; (*inout_padata)[i] != NULL; i++)
- ;
- } else
- i = 0;
-
- p = realloc(*inout_padata, (i + 2) * sizeof(krb5_pa_data *));
- if (p == NULL)
- return ENOMEM;
-
- *inout_padata = p;
-
- p[i] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
- if (p[i] == NULL)
- return ENOMEM;
- *(p[i]) = *padata;
-
- p[i + 1] = NULL;
-
- if (copy) {
- p[i]->contents = (krb5_octet *)malloc(padata->length);
- if (p[i]->contents == NULL) {
- free(p[i]);
- p[i] = NULL;
+ *out = NULL;
+ if (len > 0) {
+ buf = malloc(len);
+ if (buf == NULL)
return ENOMEM;
- }
-
- memcpy(p[i]->contents, padata->contents, padata->length);
}
+ pa = malloc(sizeof(*pa));
+ if (pa == NULL) {
+ free(buf);
+ return ENOMEM;
+ }
+ pa->magic = KV5M_PA_DATA;
+ pa->pa_type = pa_type;
+ pa->length = len;
+ pa->contents = buf;
+ *out = pa;
+ return 0;
+}
+/* Add pa to list, claiming its memory. Free pa on failure. */
+krb5_error_code
+add_pa_data_element(krb5_pa_data ***list, krb5_pa_data *pa)
+{
+ size_t count;
+ krb5_pa_data **newlist;
+
+ for (count = 0; *list != NULL && (*list)[count] != NULL; count++);
+
+ newlist = realloc(*list, (count + 2) * sizeof(*newlist));
+ if (newlist == NULL) {
+ free(pa->contents);
+ free(pa);
+ return ENOMEM;
+ }
+ newlist[count] = pa;
+ newlist[count + 1] = NULL;
+ *list = newlist;
return 0;
}
@@ -1850,38 +1844,47 @@ kdc_handle_protected_negotiation(krb5_context context,
{
krb5_error_code retval = 0;
krb5_checksum checksum;
- krb5_data *out = NULL;
- krb5_pa_data pa, *pa_in;
+ krb5_data *der_cksum = NULL;
+ krb5_pa_data *pa, *pa_in;
+
+ memset(&checksum, 0, sizeof(checksum));
+
pa_in = krb5int_find_pa_data(context, request->padata,
KRB5_ENCPADATA_REQ_ENC_PA_REP);
if (pa_in == NULL)
return 0;
- pa.magic = KV5M_PA_DATA;
- pa.pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
- memset(&checksum, 0, sizeof(checksum));
- retval = krb5_c_make_checksum(context,0, reply_key,
- KRB5_KEYUSAGE_AS_REQ, req_pkt, &checksum);
+
+ /* Compute and encode a checksum over the AS-REQ. */
+ retval = krb5_c_make_checksum(context, 0, reply_key, KRB5_KEYUSAGE_AS_REQ,
+ req_pkt, &checksum);
if (retval != 0)
goto cleanup;
- retval = encode_krb5_checksum(&checksum, &out);
+ retval = encode_krb5_checksum(&checksum, &der_cksum);
if (retval != 0)
goto cleanup;
- pa.contents = (krb5_octet *) out->data;
- pa.length = out->length;
- retval = add_pa_data_element(context, &pa, out_enc_padata, FALSE);
+
+ /* Add a pa-data element to the list, stealing memory from der_cksum. */
+ retval = alloc_pa_data(KRB5_ENCPADATA_REQ_ENC_PA_REP, 0, &pa);
if (retval)
goto cleanup;
- out->data = NULL;
- pa.magic = KV5M_PA_DATA;
- pa.pa_type = KRB5_PADATA_FX_FAST;
- pa.length = 0;
- pa.contents = NULL;
- retval = add_pa_data_element(context, &pa, out_enc_padata, FALSE);
+ pa->length = der_cksum->length;
+ pa->contents = (uint8_t *)der_cksum->data;
+ der_cksum->data = NULL;
+ /* add_pa_data_element() claims pa on success or failure. */
+ retval = add_pa_data_element(out_enc_padata, pa);
+ if (retval)
+ goto cleanup;
+
+ /* Add a zero-length PA-FX-FAST element to the list. */
+ retval = alloc_pa_data(KRB5_PADATA_FX_FAST, 0, &pa);
+ if (retval)
+ goto cleanup;
+ /* add_pa_data_element() claims pa on success or failure. */
+ retval = add_pa_data_element(out_enc_padata, pa);
+
cleanup:
- if (checksum.contents)
- krb5_free_checksum_contents(context, &checksum);
- if (out != NULL)
- krb5_free_data(context, out);
+ krb5_free_checksum_contents(context, &checksum);
+ krb5_free_data(context, der_cksum);
return retval;
}
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index c57d48f73..198eab9c4 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -202,10 +202,10 @@ void
free_padata_context(krb5_context context, void *padata_context);
krb5_error_code
-add_pa_data_element (krb5_context context,
- krb5_pa_data *padata,
- krb5_pa_data ***out_padata,
- krb5_boolean copy);
+alloc_pa_data(krb5_preauthtype pa_type, size_t len, krb5_pa_data **out);
+
+krb5_error_code
+add_pa_data_element(krb5_pa_data ***list, krb5_pa_data *pa);
/* kdc_preauth_ec.c */
krb5_error_code

View File

@ -0,0 +1,480 @@
From 4f14a2f48b52e59c472847a5522fd0cf52927755 Mon Sep 17 00:00:00 2001
From: Alexander Scheel <ascheel@redhat.com>
Date: Fri, 30 Jun 2017 16:03:01 -0400
Subject: [PATCH] Refactor krb5 GSS checksum handling
Separate out checksum handling from kg_accept_krb5() into a new helper
process_checksum().
[ghudson@mit.edu: simplified checksum processing and made it use
k5-input.h instead of TREAD_ macros; moved more flag handling into
helper]
[iboukris: adjusted helper function arguments, allowing access to the
full authenticator for subsequent changes]
(cherry picked from commit 64d56233f9816a2a93f6e8d3030c8ed6ce397735)
[rharwood@redhat.com: problem with typo fix commit, I think]
(cherry picked from commit a34b7c50e62c19f80d39ece6a72017dac781df64)
---
src/lib/gssapi/krb5/accept_sec_context.c | 383 +++++++++++------------
1 file changed, 179 insertions(+), 204 deletions(-)
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index c5bddb1e8..70dd7fc0c 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -98,6 +98,7 @@
*/
#include "k5-int.h"
+#include "k5-input.h"
#include "gssapiP_krb5.h"
#ifdef HAVE_MEMORY_H
#include <memory.h>
@@ -413,6 +414,174 @@ kg_process_extension(krb5_context context,
return code;
}
+/* The length of the MD5 channel bindings in an 0x8003 checksum */
+#define CB_MD5_LEN 16
+
+/* The minimum length of an 0x8003 checksum value (4-byte channel bindings
+ * length, 16-byte channel bindings, 4-byte flags) */
+#define MIN_8003_LEN (4 + CB_MD5_LEN + 4)
+
+/* The flags we accept from the initiator's authenticator checksum. */
+#define INITIATOR_FLAGS (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG | \
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | \
+ GSS_C_SEQUENCE_FLAG | GSS_C_DCE_STYLE | \
+ GSS_C_IDENTIFY_FLAG | GSS_C_EXTENDED_ERROR_FLAG)
+
+/*
+ * The krb5 GSS mech appropriates the authenticator checksum field from RFC
+ * 4120 to store structured data instead of a checksum, indicated with checksum
+ * type 0x8003 (see RFC 4121 section 4.1.1). Some implementations instead send
+ * no checksum, or a regular checksum over empty data.
+ *
+ * Interpret the checksum. Read delegated creds into *deleg_out if it is not
+ * NULL. Set *flags_out to the allowed subset of token flags, plus
+ * GSS_C_DELEG_FLAG if a delegated credential was present. Process any
+ * extensions found using exts. On error, set *code_out to a krb5_error code
+ * for use as a minor status value.
+ */
+static OM_uint32
+process_checksum(OM_uint32 *minor_status, krb5_context context,
+ gss_channel_bindings_t acceptor_cb,
+ krb5_auth_context auth_context, krb5_flags ap_req_options,
+ krb5_authenticator *authenticator, krb5_gss_ctx_ext_t exts,
+ krb5_gss_cred_id_t *deleg_out, krb5_ui_4 *flags_out,
+ krb5_error_code *code_out)
+{
+ krb5_error_code code = 0;
+ OM_uint32 status, option_id, token_flags;
+ size_t cb_len, option_len;
+ krb5_boolean valid;
+ krb5_key subkey;
+ krb5_data option, empty = empty_data();
+ krb5_checksum cb_cksum;
+ const uint8_t *token_cb, *option_bytes;
+ struct k5input in;
+ const krb5_checksum *cksum = authenticator->checksum;
+
+ cb_cksum.contents = NULL;
+
+ if (cksum == NULL) {
+ /*
+ * Some SMB client implementations use handcrafted GSSAPI code that
+ * does not provide a checksum. MS-KILE documents that the Microsoft
+ * implementation considers a missing checksum acceptable; the server
+ * assumes all flags are unset in this case, and does not check channel
+ * bindings.
+ */
+ *flags_out = 0;
+ } else if (cksum->checksum_type != CKSUMTYPE_KG_CB) {
+ /* Samba sends a regular checksum. */
+ code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
+ if (code) {
+ status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ /* Verifying the checksum ensures that this authenticator wasn't
+ * replayed from one with a checksum over actual data. */
+ code = krb5_k_verify_checksum(context, subkey,
+ KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM, &empty,
+ cksum, &valid);
+ krb5_k_free_key(context, subkey);
+ if (code || !valid) {
+ status = GSS_S_BAD_SIG;
+ goto fail;
+ }
+
+ /* Use ap_options from the request to guess the mutual flag. */
+ *flags_out = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED)
+ *flags_out |= GSS_C_MUTUAL_FLAG;
+ } else {
+ /* The checksum must contain at least a fixed 24-byte part. */
+ if (cksum->length < MIN_8003_LEN) {
+ status = GSS_S_BAD_BINDINGS;
+ goto fail;
+ }
+
+ k5_input_init(&in, cksum->contents, cksum->length);
+ cb_len = k5_input_get_uint32_le(&in);
+ if (cb_len != CB_MD5_LEN) {
+ code = KG_BAD_LENGTH;
+ status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ token_cb = k5_input_get_bytes(&in, cb_len);
+ if (acceptor_cb != GSS_C_NO_CHANNEL_BINDINGS) {
+ code = kg_checksum_channel_bindings(context, acceptor_cb,
+ &cb_cksum);
+ if (code) {
+ status = GSS_S_BAD_BINDINGS;
+ goto fail;
+ }
+ assert(cb_cksum.length == cb_len);
+ if (k5_bcmp(token_cb, cb_cksum.contents, cb_len) != 0) {
+ status = GSS_S_BAD_BINDINGS;
+ goto fail;
+ }
+ }
+
+ /* Read the token flags and accept some of them as context flags. */
+ token_flags = k5_input_get_uint32_le(&in);
+ *flags_out = token_flags & INITIATOR_FLAGS;
+
+ /* Read the delegated credential if present. */
+ if (in.len >= 4 && (token_flags & GSS_C_DELEG_FLAG)) {
+ option_id = k5_input_get_uint16_le(&in);
+ option_len = k5_input_get_uint16_le(&in);
+ option_bytes = k5_input_get_bytes(&in, option_len);
+ option = make_data((uint8_t *)option_bytes, option_len);
+ if (in.status) {
+ code = KG_BAD_LENGTH;
+ status = GSS_S_FAILURE;
+ goto fail;
+ }
+ if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
+ status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ /* Store the delegated credential. */
+ code = rd_and_store_for_creds(context, auth_context, &option,
+ deleg_out);
+ if (code) {
+ status = GSS_S_FAILURE;
+ goto fail;
+ }
+ *flags_out |= GSS_C_DELEG_FLAG;
+ }
+
+ /* Process any extensions at the end of the checksum. Extensions use
+ * 4-byte big-endian tag and length instead of 2-byte little-endian. */
+ while (in.len > 0) {
+ option_id = k5_input_get_uint32_be(&in);
+ option_len = k5_input_get_uint32_be(&in);
+ option_bytes = k5_input_get_bytes(&in, option_len);
+ option = make_data((uint8_t *)option_bytes, option_len);
+ if (in.status) {
+ code = KG_BAD_LENGTH;
+ status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ code = kg_process_extension(context, auth_context, option_id,
+ &option, exts);
+ if (code) {
+ status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+ }
+
+ status = GSS_S_COMPLETE;
+
+fail:
+ free(cb_cksum.contents);
+ *code_out = code;
+ return status;
+}
+
static OM_uint32
kg_accept_krb5(minor_status, context_handle,
verifier_cred_handle, input_token,
@@ -433,17 +602,13 @@ kg_accept_krb5(minor_status, context_handle,
krb5_gss_ctx_ext_t exts;
{
krb5_context context;
- unsigned char *ptr, *ptr2;
+ unsigned char *ptr;
char *sptr;
- OM_uint32 tmp;
- size_t md5len;
krb5_gss_cred_id_t cred = 0;
krb5_data ap_rep, ap_req;
- unsigned int i;
krb5_error_code code;
krb5_address addr, *paddr;
krb5_authenticator *authdat = 0;
- krb5_checksum reqcksum;
krb5_gss_name_t name = NULL;
krb5_ui_4 gss_flags = 0;
krb5_gss_ctx_id_rec *ctx = NULL;
@@ -451,8 +616,6 @@ kg_accept_krb5(minor_status, context_handle,
gss_buffer_desc token;
krb5_auth_context auth_context = NULL;
krb5_ticket * ticket = NULL;
- int option_id;
- krb5_data option;
const gss_OID_desc *mech_used = NULL;
OM_uint32 major_status = GSS_S_FAILURE;
OM_uint32 tmp_minor_status;
@@ -463,7 +626,6 @@ kg_accept_krb5(minor_status, context_handle,
krb5int_access kaccess;
int cred_rcache = 0;
int no_encap = 0;
- int token_deleg_flag = 0;
krb5_flags ap_req_options = 0;
krb5_enctype negotiated_etype;
krb5_authdata_context ad_context = NULL;
@@ -489,7 +651,6 @@ kg_accept_krb5(minor_status, context_handle,
output_token->length = 0;
output_token->value = NULL;
token.value = 0;
- reqcksum.contents = 0;
ap_req.data = 0;
ap_rep.data = 0;
@@ -654,195 +815,16 @@ kg_accept_krb5(minor_status, context_handle,
krb5_auth_con_getauthenticator(context, auth_context, &authdat);
- if (authdat->checksum == NULL) {
- /*
- * Some SMB client implementations use handcrafted GSSAPI code that
- * does not provide a checksum. MS-KILE documents that the Microsoft
- * implementation considers a missing checksum acceptable; the server
- * assumes all flags are unset in this case, and does not check channel
- * bindings.
- */
- gss_flags = 0;
- } else if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) {
- /* Samba does not send 0x8003 GSS-API checksums */
- krb5_boolean valid;
- krb5_key subkey;
- krb5_data zero;
+ major_status = process_checksum(minor_status, context, input_chan_bindings,
+ auth_context, ap_req_options,
+ authdat, exts,
+ delegated_cred_handle ? &deleg_cred : NULL,
+ &gss_flags, &code);
- code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
- if (code) {
- major_status = GSS_S_FAILURE;
- goto fail;
- }
+ if (major_status != GSS_S_COMPLETE)
+ goto fail;
- zero.length = 0;
- zero.data = "";
-
- code = krb5_k_verify_checksum(context,
- subkey,
- KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
- &zero,
- authdat->checksum,
- &valid);
- krb5_k_free_key(context, subkey);
- if (code || !valid) {
- major_status = GSS_S_BAD_SIG;
- goto fail;
- }
-
- /* Use ap_options from the request to guess the mutual flag. */
- gss_flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
- if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED)
- gss_flags |= GSS_C_MUTUAL_FLAG;
- } else {
- /* gss krb5 v1 */
-
- /* stash this now, for later. */
- code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
- if (code) {
- major_status = GSS_S_FAILURE;
- goto fail;
- }
-
- /* verify that the checksum is correct */
-
- /*
- The checksum may be either exactly 24 bytes, in which case
- no options are specified, or greater than 24 bytes, in which case
- one or more options are specified. Currently, the only valid
- option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
- */
-
- if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
- (authdat->checksum->length < 24)) {
- code = 0;
- major_status = GSS_S_BAD_BINDINGS;
- goto fail;
- }
-
- ptr = (unsigned char *) authdat->checksum->contents;
-
- TREAD_INT(ptr, tmp, 0);
-
- if (tmp != md5len) {
- code = KG_BAD_LENGTH;
- major_status = GSS_S_FAILURE;
- goto fail;
- }
-
- /*
- The following section of code attempts to implement the
- optional channel binding facility as described in RFC2743.
-
- Since this facility is optional channel binding may or may
- not have been provided by either the client or the server.
-
- If the server has specified input_chan_bindings equal to
- GSS_C_NO_CHANNEL_BINDINGS then we skip the check. If
- the server does provide channel bindings then we compute
- a checksum and compare against those provided by the
- client. */
-
- if ((code = kg_checksum_channel_bindings(context,
- input_chan_bindings,
- &reqcksum))) {
- major_status = GSS_S_BAD_BINDINGS;
- goto fail;
- }
-
- /* Always read the clients bindings - eventhough we might ignore them */
- TREAD_STR(ptr, ptr2, reqcksum.length);
-
- if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
- if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
- xfree(reqcksum.contents);
- reqcksum.contents = 0;
- code = 0;
- major_status = GSS_S_BAD_BINDINGS;
- goto fail;
- }
-
- }
-
- xfree(reqcksum.contents);
- reqcksum.contents = 0;
-
- /* Read the token flags. Remember if GSS_C_DELEG_FLAG was set, but
- * mask it out until we actually read a delegated credential. */
- TREAD_INT(ptr, gss_flags, 0);
- token_deleg_flag = (gss_flags & GSS_C_DELEG_FLAG);
- gss_flags &= ~GSS_C_DELEG_FLAG;
-
- /* if the checksum length > 24, there are options to process */
-
- i = authdat->checksum->length - 24;
- if (i && token_deleg_flag) {
- if (i >= 4) {
- TREAD_INT16(ptr, option_id, 0);
- TREAD_INT16(ptr, option.length, 0);
- i -= 4;
-
- if (i < option.length) {
- code = KG_BAD_LENGTH;
- major_status = GSS_S_FAILURE;
- goto fail;
- }
-
- /* have to use ptr2, since option.data is wrong type and
- macro uses ptr as both lvalue and rvalue */
-
- TREAD_STR(ptr, ptr2, option.length);
- option.data = (char *) ptr2;
-
- i -= option.length;
-
- if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
- major_status = GSS_S_FAILURE;
- goto fail;
- }
-
- /* store the delegated credential */
-
- code = rd_and_store_for_creds(context, auth_context, &option,
- (delegated_cred_handle) ?
- &deleg_cred : NULL);
- if (code) {
- major_status = GSS_S_FAILURE;
- goto fail;
- }
-
- gss_flags |= GSS_C_DELEG_FLAG;
- } /* if i >= 4 */
- /* ignore any additional trailing data, for now */
- }
- while (i > 0) {
- /* Process Type-Length-Data options */
- if (i < 8) {
- code = KG_BAD_LENGTH;
- major_status = GSS_S_FAILURE;
- goto fail;
- }
- TREAD_INT(ptr, option_id, 1);
- TREAD_INT(ptr, option.length, 1);
- i -= 8;
- if (i < option.length) {
- code = KG_BAD_LENGTH;
- major_status = GSS_S_FAILURE;
- goto fail;
- }
- TREAD_STR(ptr, ptr2, option.length);
- option.data = (char *)ptr2;
-
- i -= option.length;
-
- code = kg_process_extension(context, auth_context,
- option_id, &option, exts);
- if (code != 0) {
- major_status = GSS_S_FAILURE;
- goto fail;
- }
- }
- }
+ major_status = GSS_S_FAILURE;
if (exts->iakerb.conv && !exts->iakerb.verified) {
major_status = GSS_S_BAD_SIG;
@@ -869,12 +851,7 @@ kg_accept_krb5(minor_status, context_handle,
ctx->mech_used = (gss_OID) mech_used;
ctx->auth_context = auth_context;
ctx->initiate = 0;
- ctx->gss_flags = (GSS_C_TRANS_FLAG |
- ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
- GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
- GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
- GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
- GSS_C_EXTENDED_ERROR_FLAG)));
+ ctx->gss_flags = gss_flags | GSS_C_TRANS_FLAG;
ctx->seed_init = 0;
ctx->cred_rcache = cred_rcache;
@@ -1161,8 +1138,6 @@ fail:
krb5_auth_con_free(context, auth_context);
}
- if (reqcksum.contents)
- xfree(reqcksum.contents);
if (ap_rep.data)
krb5_free_data_contents(context, &ap_rep);
if (major_status == GSS_S_COMPLETE ||

View File

@ -0,0 +1,79 @@
From cb8c8af56d306267d6964da217c65e129fe83c82 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Wed, 26 Feb 2020 18:27:17 -0500
Subject: [PATCH] Refresh manually acquired creds from client keytab
If a client keytab is present but credentials are acquired manually,
the credentials would not be refreshed because no refresh_time config
var is set in the cache. Change kg_cred_time_to_refresh() to attempt
a refresh from the client keytab on any credentials which will expire
in the next 30 seconds.
[ghudson@mit.edu: adjused code and added test case]
ticket: 7976
(cherry picked from commit 729896467e3c77904666019d6cbbda583ae49b95)
(cherry picked from commit 685aada9eae420cb5156ca7b71c2c7614c0b6e2c)
---
src/lib/gssapi/krb5/acquire_cred.c | 14 +++++++++++---
src/tests/gssapi/t_client_keytab.py | 18 ++++++++++++++++++
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c
index acc1868f8..4062f4741 100644
--- a/src/lib/gssapi/krb5/acquire_cred.c
+++ b/src/lib/gssapi/krb5/acquire_cred.c
@@ -557,15 +557,23 @@ set_refresh_time(krb5_context context, krb5_ccache ccache,
krb5_boolean
kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred)
{
- krb5_timestamp now;
+ krb5_timestamp now, soon;
if (krb5_timeofday(context, &now))
return FALSE;
+ soon = ts_incr(now, 30);
if (cred->refresh_time != 0 && !ts_after(cred->refresh_time, now)) {
- set_refresh_time(context, cred->ccache,
- ts_incr(cred->refresh_time, 30));
+ set_refresh_time(context, cred->ccache, soon);
return TRUE;
}
+
+ /* If the creds will expire soon, try to refresh even if they weren't
+ * acquired with a client keytab. */
+ if (ts_after(soon, cred->expire)) {
+ set_refresh_time(context, cred->ccache, soon);
+ return TRUE;
+ }
+
return FALSE;
}
diff --git a/src/tests/gssapi/t_client_keytab.py b/src/tests/gssapi/t_client_keytab.py
index e474a27c7..7847b3ecd 100755
--- a/src/tests/gssapi/t_client_keytab.py
+++ b/src/tests/gssapi/t_client_keytab.py
@@ -124,4 +124,22 @@ realm.kinit(realm.user_princ, password('user'))
realm.run(['./t_ccselect', phost], env=bad_cktname,
expected_msg=realm.user_princ)
+mark('refresh of manually acquired creds')
+
+# Test 17: no name/ccache specified, manually acquired creds which
+# will expire soon. Verify that creds are refreshed using the current
+# client name, with refresh_time set in the refreshed ccache.
+realm.kinit('bob', password('bob'), ['-l', '15s'])
+realm.run(['./t_ccselect', phost], expected_msg='bob')
+realm.run([klist, '-C'], expected_msg='refresh_time = ')
+
+# Test 18: no name/ccache specified, manually acquired creds with a
+# client principal not present in the client keytab. A refresh is
+# attempted but fails, and an expired ticket error results.
+realm.kinit(realm.admin_princ, password('admin'), ['-l', '-1s'])
+msgs = ('Getting initial credentials for user/admin@KRBTEST.COM',
+ '/Matching credential not found')
+realm.run(['./t_ccselect', phost], expected_code=1,
+ expected_msg='Ticket expired', expected_trace=msgs)
+
success('Client keytab tests')

File diff suppressed because it is too large Load Diff

View File

@ -1,59 +0,0 @@
From 2a96564f6fd53f2e1e8424d865c02349bfe5b818 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Sat, 15 Dec 2018 11:56:36 +0200
Subject: [PATCH] Remove incorrect KDC assertion
The assertion in return_enc_padata() is reachable because
kdc_make_s4u2self_rep() may have previously added encrypted padata.
It is no longer necessary because the code uses add_pa_data_element()
instead of allocating a new list.
CVE-2018-20217:
In MIT krb5 1.8 or later, an authenticated user who can obtain a TGT
using an older encryption type (DES, DES3, or RC4) can cause an
assertion failure in the KDC by sending an S4U2Self request.
[ghudson@mit.edu: rewrote commit message with CVE description]
(cherry picked from commit 94e5eda5bb94d1d44733a49c3d9b6d1e42c74def)
ticket: 8767
version_fixed: 1.16.3
(cherry picked from commit 56870f9456da78d77a667dfc03a6d90f948dc3a5)
---
src/kdc/kdc_preauth.c | 1 -
src/tests/gssapi/t_s4u.py | 7 +++++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 811c16368..6f0cf68d9 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -1666,7 +1666,6 @@ return_enc_padata(krb5_context context, krb5_data *req_pkt,
krb5_error_code code = 0;
/* This should be initialized and only used for Win2K compat and other
* specific standardized uses such as FAST negotiation. */
- assert(reply_encpart->enc_padata == NULL);
if (is_referral) {
code = return_referral_enc_padata(context, reply_encpart, server);
if (code)
diff --git a/src/tests/gssapi/t_s4u.py b/src/tests/gssapi/t_s4u.py
index fc9d9e8a4..f65000453 100755
--- a/src/tests/gssapi/t_s4u.py
+++ b/src/tests/gssapi/t_s4u.py
@@ -139,6 +139,13 @@ if 'auth1: user@' not in out or 'auth2: user@' not in out:
realm.stop()
+for realm in multipass_realms(create_host=False, get_creds=False):
+ service1 = 'service/1@%s' % realm.realm
+ realm.addprinc(service1)
+ realm.extract_keytab(service1, realm.keytab)
+ realm.kinit(service1, None, ['-k'])
+ realm.run(['./t_s4u', 'p:user', '-'])
+
# Exercise cross-realm S4U2Self. The query in the foreign realm will
# fail, but we can check that the right server principal was used.
r1, r2 = cross_realms(2, create_user=False)

View File

@ -1,45 +0,0 @@
From 83da5675551dba13fee837adc26ce885a061dbc1 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 3 May 2018 14:40:45 -0400
Subject: [PATCH] Remove "-nodes" option from make-certs scripts
The openssl command does not recognize options after positional
arguments, so in "openssl genrsa $KEYSIZE -nodes", the "-nodes" was
ignored as a excess positional argument prior to OpenSSL 1.1.0h, and
now causes an error. "-nodes" is an option to the openssl req and
pkcs12 subcommands, but genrsa creates unencrypted keys by default.
[ghudson@mit.edu: edited commit message]
(cherry picked from commit 928a36aae326d496c9a73f2cd41b4da45eef577c)
---
src/tests/dejagnu/pkinit-certs/make-certs.sh | 2 +-
src/tests/dejagnu/proxy-certs/make-certs.sh | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/tests/dejagnu/pkinit-certs/make-certs.sh b/src/tests/dejagnu/pkinit-certs/make-certs.sh
index 63f0c6f75..387311aed 100755
--- a/src/tests/dejagnu/pkinit-certs/make-certs.sh
+++ b/src/tests/dejagnu/pkinit-certs/make-certs.sh
@@ -114,7 +114,7 @@ extendedKeyUsage = $CLIENT_EKU_LIST
EOF
# Generate a private key.
-openssl genrsa $KEYSIZE -nodes > privkey.pem
+openssl genrsa $KEYSIZE > privkey.pem
openssl rsa -in privkey.pem -out privkey-enc.pem -des3 -passout pass:encrypted
# Generate a "CA" certificate.
diff --git a/src/tests/dejagnu/proxy-certs/make-certs.sh b/src/tests/dejagnu/proxy-certs/make-certs.sh
index 1191bf05e..24ef91bde 100755
--- a/src/tests/dejagnu/proxy-certs/make-certs.sh
+++ b/src/tests/dejagnu/proxy-certs/make-certs.sh
@@ -79,7 +79,7 @@ extendedKeyUsage = $PROXY_EKU_LIST
EOF
# Generate a private key.
-openssl genrsa $KEYSIZE -nodes > privkey.pem
+openssl genrsa $KEYSIZE > privkey.pem
# Generate a "CA" certificate.
SUBJECT=signer openssl req -config openssl.cnf -new -x509 -extensions exts_ca \

View File

@ -1,37 +0,0 @@
From 65130d13c59c13b7e5e07cfe69421ce1a08c0b7f Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 17 Jul 2018 11:33:03 -0400
Subject: [PATCH] Remove outdated note in krb5kdc man page
Commit af5b77c887bfff24603715f8296c00d5eb839b0c (ticket 8348) removed
the interface-scanning workaround for platforms without pktinfo
support, so there is no longer an interaction between the krb5kdc -w
option and this workaround.
ticket: 8716 (new)
tags: pullup
target_version: 1.16-next
(cherry picked from commit 728b66ab867e31c4c338c6a6309d629d39a4ec3f)
---
doc/admin/admin_commands/krb5kdc.rst | 7 -------
1 file changed, 7 deletions(-)
diff --git a/doc/admin/admin_commands/krb5kdc.rst b/doc/admin/admin_commands/krb5kdc.rst
index bda2c015c..b605b563d 100644
--- a/doc/admin/admin_commands/krb5kdc.rst
+++ b/doc/admin/admin_commands/krb5kdc.rst
@@ -72,13 +72,6 @@ will relay SIGHUP signals to the worker subprocesses, and will
terminate the worker subprocess if the it is itself terminated or if
any other worker process exits.
-.. note::
-
- On operating systems which do not have *pktinfo* support,
- using worker processes will prevent the KDC from listening
- for UDP packets on network interfaces created after the KDC
- starts.
-
The **-x** *db_args* option specifies database-specific arguments.
See :ref:`Database Options <dboptions>` in :ref:`kadmin(1)` for
supported arguments.

View File

@ -1,27 +0,0 @@
From 3b3e31316ae247e18ea22293dffbc8f604338fa7 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sat, 17 Mar 2018 22:47:34 -0400
Subject: [PATCH] Report extended errors in kinit -k -t KDB:
In kinit, if we recreate the context using kinit_kdb_init(), also
reset the global errctx so that we use the new context to retrieve
extended error messages.
ticket: 8652 (new)
(cherry picked from commit d4d902d317a2acc46ee71094a33a9203b6135275)
---
src/clients/kinit/kinit.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c
index a518284ea..3fdae2878 100644
--- a/src/clients/kinit/kinit.c
+++ b/src/clients/kinit/kinit.c
@@ -718,6 +718,7 @@ k5_kinit(struct k_opts *opts, struct k5_data *k5)
#ifndef _WIN32
if (strncmp(opts->keytab_name, "KDB:", 4) == 0) {
ret = kinit_kdb_init(&k5->ctx, k5->me->realm.data);
+ errctx = k5->ctx;
if (ret) {
com_err(progname, ret,
_("while setting up KDB keytab for realm %s"),

View File

@ -1,491 +0,0 @@
From 70f41a8dafaadfb43aba4918564c22460f812dca Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 5 Apr 2018 16:23:34 -0400
Subject: [PATCH] Restrict pre-authentication fallback cases
Add a new callback disable_fallback() and call it from each clpreauth
module when it generates a client message using credentials to
authenticate. (For SPAKE, this is the message responding to a
challenge; for all other current mechanisms, it is the first and only
client message.) If disable_fallback() is called, do not try another
mechanism after a KDC error.
Remove k5_reset_preauth_types_tried() and its call sites, so that
preauth mechanisms which are tried optimistically will no longer be
retried after a failure.
ticket: 8654
(cherry picked from commit 7a24a088c16d326127dd2b29084d4ca085c70d10)
---
src/include/krb5/clpreauth_plugin.h | 14 ++++
src/lib/krb5/krb/get_in_tkt.c | 21 +++---
src/lib/krb5/krb/init_creds_ctx.h | 1 +
src/lib/krb5/krb/int-proto.h | 3 -
src/lib/krb5/krb/preauth2.c | 23 +++----
src/lib/krb5/krb/preauth_ec.c | 1 +
src/lib/krb5/krb/preauth_encts.c | 2 +
src/lib/krb5/krb/preauth_otp.c | 4 ++
src/lib/krb5/krb/preauth_sam2.c | 1 +
src/plugins/preauth/pkinit/pkinit_clnt.c | 1 +
src/plugins/preauth/spake/spake_client.c | 4 ++
src/plugins/preauth/test/cltest.c | 11 +++
src/tests/t_preauth.py | 88 +++++++++++++++++++++---
src/tests/t_spake.py | 9 +--
14 files changed, 134 insertions(+), 49 deletions(-)
diff --git a/src/include/krb5/clpreauth_plugin.h b/src/include/krb5/clpreauth_plugin.h
index 0106734ad..5317669b7 100644
--- a/src/include/krb5/clpreauth_plugin.h
+++ b/src/include/krb5/clpreauth_plugin.h
@@ -160,7 +160,21 @@ typedef struct krb5_clpreauth_callbacks_st {
krb5_error_code (*set_cc_config)(krb5_context context,
krb5_clpreauth_rock rock,
const char *key, const char *data);
+
/* End of version 2 clpreauth callbacks (added in 1.11). */
+
+ /*
+ * Prevent further fallbacks to other preauth mechanisms if the KDC replies
+ * with an error. (The module itself can still respond to errors with its
+ * tryagain method, or continue after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED
+ * errors with its process method.) A module should invoke this callback
+ * from the process method when it generates an authenticated request using
+ * credentials; often this will be the first or only client message
+ * generated by the mechanism.
+ */
+ void (*disable_fallback)(krb5_context context, krb5_clpreauth_rock rock);
+
+ /* End of version 3 clpreauth callbacks (added in 1.17). */
} *krb5_clpreauth_callbacks;
/*
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 1d96ff163..c026bbc6d 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -1331,9 +1331,7 @@ init_creds_step_request(krb5_context context,
krb5_free_pa_data(context, ctx->optimistic_padata);
ctx->optimistic_padata = NULL;
if (code) {
- /* Make an unauthenticated request, and possibly try again using
- * the same mechanisms as we tried optimistically. */
- k5_reset_preauth_types_tried(ctx);
+ /* Make an unauthenticated request. */
krb5_clear_error_message(context);
code = 0;
}
@@ -1361,6 +1359,9 @@ init_creds_step_request(krb5_context context,
/* Don't continue after a keyboard interrupt. */
if (code == KRB5_LIBOS_PWDINTR)
goto cleanup;
+ /* Don't continue if fallback is disabled. */
+ if (code && ctx->fallback_disabled)
+ goto cleanup;
if (code) {
/* See if we can try a different preauth mech before giving up. */
k5_save_ctx_error(context, code, &save);
@@ -1549,16 +1550,10 @@ init_creds_step_reply(krb5_context context,
} else if (reply_code == KDC_ERR_PREAUTH_FAILED && retry) {
note_req_timestamp(context, ctx, ctx->err_reply->stime,
ctx->err_reply->susec);
- if (ctx->method_padata == NULL) {
- /* Optimistic preauth failed on the KDC. Allow all mechanisms
- * to be tried again using method data. */
- k5_reset_preauth_types_tried(ctx);
- } else {
- /* Don't try again with the mechanism that failed. */
- code = k5_preauth_note_failed(ctx, ctx->selected_preauth_type);
- if (code)
- goto cleanup;
- }
+ /* Don't try again with the mechanism that failed. */
+ code = k5_preauth_note_failed(ctx, ctx->selected_preauth_type);
+ if (code)
+ goto cleanup;
ctx->selected_preauth_type = KRB5_PADATA_NONE;
/* Accept or update method data if the KDC sent it. */
if (ctx->err_padata != NULL)
diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
index b19410a13..7ba61e17c 100644
--- a/src/lib/krb5/krb/init_creds_ctx.h
+++ b/src/lib/krb5/krb/init_creds_ctx.h
@@ -60,6 +60,7 @@ struct _krb5_init_creds_context {
krb5_enctype etype;
krb5_boolean info_pa_permitted;
krb5_boolean restarted;
+ krb5_boolean fallback_disabled;
struct krb5_responder_context_st rctx;
krb5_preauthtype selected_preauth_type;
krb5_preauthtype allowed_preauth_type;
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index cda9010e3..d20133885 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -197,9 +197,6 @@ k5_init_preauth_context(krb5_context context);
void
k5_free_preauth_context(krb5_context context);
-void
-k5_reset_preauth_types_tried(krb5_init_creds_context ctx);
-
krb5_error_code
k5_preauth_note_failed(krb5_init_creds_context ctx, krb5_preauthtype pa_type);
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c
index 451e0b7a8..1f17ec2b0 100644
--- a/src/lib/krb5/krb/preauth2.c
+++ b/src/lib/krb5/krb/preauth2.c
@@ -203,18 +203,6 @@ cleanup:
free_handles(context, list);
}
-/* Reset the memory of which preauth types we have already tried. */
-void
-k5_reset_preauth_types_tried(krb5_init_creds_context ctx)
-{
- krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
-
- if (reqctx == NULL)
- return;
- free(reqctx->failed);
- reqctx->failed = NULL;
-}
-
/* Add pa_type to the list of types which has previously failed. */
krb5_error_code
k5_preauth_note_failed(krb5_init_creds_context ctx, krb5_preauthtype pa_type)
@@ -553,8 +541,14 @@ set_cc_config(krb5_context context, krb5_clpreauth_rock rock,
return ret;
}
+static void
+disable_fallback(krb5_context context, krb5_clpreauth_rock rock)
+{
+ ((krb5_init_creds_context)rock)->fallback_disabled = TRUE;
+}
+
static struct krb5_clpreauth_callbacks_st callbacks = {
- 2,
+ 3,
get_etype,
fast_armor,
get_as_key,
@@ -564,7 +558,8 @@ static struct krb5_clpreauth_callbacks_st callbacks = {
responder_get_answer,
need_as_key,
get_cc_config,
- set_cc_config
+ set_cc_config,
+ disable_fallback
};
/* Tweak the request body, for now adding any enctypes which the module claims
diff --git a/src/lib/krb5/krb/preauth_ec.c b/src/lib/krb5/krb/preauth_ec.c
index c1aa9090f..75aab770e 100644
--- a/src/lib/krb5/krb/preauth_ec.c
+++ b/src/lib/krb5/krb/preauth_ec.c
@@ -138,6 +138,7 @@ ec_process(krb5_context context, krb5_clpreauth_moddata moddata,
encoded_ts->data = NULL;
*out_padata = pa;
pa = NULL;
+ cb->disable_fallback(context, rock);
}
free(pa);
krb5_free_data(context, encoded_ts);
diff --git a/src/lib/krb5/krb/preauth_encts.c b/src/lib/krb5/krb/preauth_encts.c
index cec384227..45bf9da92 100644
--- a/src/lib/krb5/krb/preauth_encts.c
+++ b/src/lib/krb5/krb/preauth_encts.c
@@ -109,6 +109,8 @@ encts_process(krb5_context context, krb5_clpreauth_moddata moddata,
*out_padata = pa;
pa = NULL;
+ cb->disable_fallback(context, rock);
+
cleanup:
krb5_free_data(context, ts);
krb5_free_data(context, enc_ts);
diff --git a/src/lib/krb5/krb/preauth_otp.c b/src/lib/krb5/krb/preauth_otp.c
index 48fcbb5d5..13e584657 100644
--- a/src/lib/krb5/krb/preauth_otp.c
+++ b/src/lib/krb5/krb/preauth_otp.c
@@ -1123,6 +1123,10 @@ otp_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
/* Encode the request into the pa_data output. */
retval = set_pa_data(req, pa_data_out);
+ if (retval != 0)
+ goto error;
+ cb->disable_fallback(context, rock);
+
error:
krb5_free_data_contents(context, &value);
krb5_free_data_contents(context, &pin);
diff --git a/src/lib/krb5/krb/preauth_sam2.c b/src/lib/krb5/krb/preauth_sam2.c
index c8a330655..4c70021a9 100644
--- a/src/lib/krb5/krb/preauth_sam2.c
+++ b/src/lib/krb5/krb/preauth_sam2.c
@@ -410,6 +410,7 @@ sam2_process(krb5_context context, krb5_clpreauth_moddata moddata,
sam_padata[1] = NULL;
*out_padata = sam_padata;
+ cb->disable_fallback(context, rock);
return(0);
}
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index 9483d69e5..77e9e5308 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -179,6 +179,7 @@ pa_pkinit_gen_req(krb5_context context,
*out_padata = return_pa_data;
return_pa_data = NULL;
+ cb->disable_fallback(context, rock);
cleanup:
krb5_free_data(context, der_req);
diff --git a/src/plugins/preauth/spake/spake_client.c b/src/plugins/preauth/spake/spake_client.c
index 47a6ba26c..00734a13b 100644
--- a/src/plugins/preauth/spake/spake_client.c
+++ b/src/plugins/preauth/spake/spake_client.c
@@ -278,6 +278,10 @@ process_challenge(krb5_context context, groupstate *gstate, reqstate *st,
goto cleanup;
TRACE_SPAKE_SEND_RESPONSE(context);
ret = convert_to_padata(response, pa_out);
+ if (ret)
+ goto cleanup;
+
+ cb->disable_fallback(context, rock);
cleanup:
krb5_free_keyblock(context, k0);
diff --git a/src/plugins/preauth/test/cltest.c b/src/plugins/preauth/test/cltest.c
index f5f7c5aba..51b848481 100644
--- a/src/plugins/preauth/test/cltest.c
+++ b/src/plugins/preauth/test/cltest.c
@@ -53,6 +53,9 @@
* - If the "fail_optimistic", "fail_2rt", or "fail_tryagain" gic options are
* set, it fails with a recognizable error string at the requested point in
* processing.
+ *
+ * - If the "disable_fallback" gic option is set, fallback is disabled when a
+ * client message is generated.
*/
#include "k5-int.h"
@@ -66,6 +69,7 @@ struct client_state {
krb5_boolean fail_optimistic;
krb5_boolean fail_2rt;
krb5_boolean fail_tryagain;
+ krb5_boolean disable_fallback;
};
struct client_request_state {
@@ -81,6 +85,7 @@ test_init(krb5_context context, krb5_clpreauth_moddata *moddata_out)
assert(st != NULL);
st->indicators = NULL;
st->fail_optimistic = st->fail_2rt = st->fail_tryagain = FALSE;
+ st->disable_fallback = FALSE;
*moddata_out = (krb5_clpreauth_moddata)st;
return 0;
}
@@ -138,6 +143,8 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
return KRB5_PREAUTH_FAILED;
}
*out_pa_data = make_pa_list("optimistic", 10);
+ if (st->disable_fallback)
+ cb->disable_fallback(context, rock);
return 0;
} else if (reqst->second_round_trip) {
printf("2rt: %.*s\n", pa_data->length, pa_data->contents);
@@ -166,6 +173,8 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
indstr = (st->indicators != NULL) ? st->indicators : "";
*out_pa_data = make_pa_list(indstr, strlen(indstr));
+ if (st->disable_fallback)
+ cb->disable_fallback(context, rock);
return 0;
}
@@ -212,6 +221,8 @@ test_gic_opt(krb5_context kcontext, krb5_clpreauth_moddata moddata,
st->fail_2rt = TRUE;
} else if (strcmp(attr, "fail_tryagain") == 0) {
st->fail_tryagain = TRUE;
+ } else if (strcmp(attr, "disable_fallback") == 0) {
+ st->disable_fallback = TRUE;
}
return 0;
}
diff --git a/src/tests/t_preauth.py b/src/tests/t_preauth.py
index efb3ea20d..32e35b08b 100644
--- a/src/tests/t_preauth.py
+++ b/src/tests/t_preauth.py
@@ -37,8 +37,8 @@ expected_trace = ('Attempting optimistic preauth',
realm.run(['./icred', '-o', '-123', realm.user_princ, password('user')],
expected_trace=expected_trace)
-# Test optimistic preauth failing on client, followed by successful
-# preauth using the same module.
+# Test optimistic preauth failing on client, falling back to encrypted
+# timestamp.
msgs = ('Attempting optimistic preauth',
'Processing preauth types: -123',
'/induced optimistic fail',
@@ -46,15 +46,15 @@ msgs = ('Attempting optimistic preauth',
'/Additional pre-authentication required',
'Preauthenticating using KDC method data',
'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ 'Encrypted timestamp (for ',
+ 'module encrypted_timestamp (2) (real) returned: 0/Success',
+ 'preauth for next request: PA-FX-COOKIE (133), PA-ENC-TIMESTAMP (2)',
'Decrypted AS reply')
realm.run(['./icred', '-o', '-123', '-X', 'fail_optimistic', realm.user_princ,
- password('user')], expected_msg='testval',
- expected_trace=msgs)
+ password('user')], expected_trace=msgs)
-# Test optimistic preauth failing on KDC, followed by successful preauth
-# using the same module.
+# Test optimistic preauth failing on KDC, falling back to encrypted
+# timestamp.
realm.run([kadminl, 'setstr', realm.user_princ, 'failopt', 'yes'])
msgs = ('Attempting optimistic preauth',
'Processing preauth types: -123',
@@ -63,11 +63,24 @@ msgs = ('Attempting optimistic preauth',
'/Preauthentication failed',
'Preauthenticating using KDC method data',
'Processing preauth types:',
- 'Preauth module test (-123) (real) returned: 0/Success',
- 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ 'Encrypted timestamp (for ',
+ 'module encrypted_timestamp (2) (real) returned: 0/Success',
+ 'preauth for next request: PA-FX-COOKIE (133), PA-ENC-TIMESTAMP (2)',
'Decrypted AS reply')
realm.run(['./icred', '-o', '-123', realm.user_princ, password('user')],
- expected_msg='testval', expected_trace=msgs)
+ expected_trace=msgs)
+# Leave failopt set for the next test.
+
+# Test optimistic preauth failing on KDC, stopping because the test
+# module disabled fallback.
+msgs = ('Attempting optimistic preauth',
+ 'Processing preauth types: -123',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: -123',
+ '/Preauthentication failed')
+realm.run(['./icred', '-X', 'disable_fallback', '-o', '-123', realm.user_princ,
+ password('user')], expected_code=1,
+ expected_msg='Preauthentication failed', expected_trace=msgs)
realm.run([kadminl, 'delstr', realm.user_princ, 'failopt'])
# Test KDC_ERR_MORE_PREAUTH_DATA_REQUIRED and secure cookies.
@@ -107,6 +120,23 @@ msgs = ('Sending unauthenticated request',
realm.run(['./icred', '-X', 'fail_2rt', realm.user_princ, password('user')],
expected_msg='2rt: secondtrip', expected_trace=msgs)
+# Test client-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
+# stopping because the test module disabled fallback.
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/More preauthentication data is required',
+ 'Continuing preauth mech -123',
+ 'Processing preauth types: -123, PA-FX-COOKIE (133)',
+ '/induced 2rt fail')
+realm.run(['./icred', '-X', 'fail_2rt', '-X', 'disable_fallback',
+ realm.user_princ, password('user')], expected_code=1,
+ expected_msg='Pre-authentication failed: induced 2rt fail',
+ expected_trace=msgs)
+
# Test KDC-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
# falling back to encrypted timestamp.
realm.run([kadminl, 'setstr', realm.user_princ, 'fail2rt', 'yes'])
@@ -130,6 +160,25 @@ msgs = ('Sending unauthenticated request',
'Decrypted AS reply')
realm.run(['./icred', realm.user_princ, password('user')],
expected_msg='2rt: secondtrip', expected_trace=msgs)
+# Leave fail2rt set for the next test.
+
+# Test KDC-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
+# stopping because the test module disabled fallback.
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/More preauthentication data is required',
+ 'Continuing preauth mech -123',
+ 'Processing preauth types: -123, PA-FX-COOKIE (133)',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/Preauthentication failed')
+realm.run(['./icred', '-X', 'disable_fallback',
+ realm.user_princ, password('user')], expected_code=1,
+ expected_msg='Preauthentication failed', expected_trace=msgs)
realm.run([kadminl, 'delstr', realm.user_princ, 'fail2rt'])
# Test tryagain flow by inducing a KDC_ERR_ENCTYPE_NOSUPP error on the KDC.
@@ -170,6 +219,23 @@ msgs = ('Sending unauthenticated request',
realm.run(['./icred', '-X', 'fail_tryagain', realm.user_princ,
password('user')], expected_trace=msgs)
+# Test a client-side tryagain failure, stopping because the test
+# module disabled fallback.
+msgs = ('Sending unauthenticated request',
+ '/Additional pre-authentication required',
+ 'Preauthenticating using KDC method data',
+ 'Processing preauth types:',
+ 'Preauth module test (-123) (real) returned: 0/Success',
+ 'Produced preauth for next request: PA-FX-COOKIE (133), -123',
+ '/KDC has no support for encryption type',
+ 'Recovering from KDC error 14 using preauth mech -123',
+ 'Preauth tryagain input types (-123): -123, PA-FX-COOKIE (133)',
+ '/induced tryagain fail')
+realm.run(['./icred', '-X', 'fail_tryagain', '-X', 'disable_fallback',
+ realm.user_princ, password('user')], expected_code=1,
+ expected_msg='KDC has no support for encryption type',
+ expected_trace=msgs)
+
# Test that multiple stepwise initial creds operations can be
# performed with the same krb5_context, with proper tracking of
# clpreauth module request handles.
diff --git a/src/tests/t_spake.py b/src/tests/t_spake.py
index a81a238b4..5b47e62d3 100644
--- a/src/tests/t_spake.py
+++ b/src/tests/t_spake.py
@@ -31,9 +31,7 @@ for gnum, gname in groups:
'Decrypted AS reply')
realm.kinit('user', 'pw', expected_trace=msgs)
- # Test an unsuccessful authentication. (The client will try
- # again with encrypted timestamp, which isn't really desired,
- # but check for that as long as it is expected.)
+ # Test an unsuccessful authentication.
msgs = ('/Additional pre-authentication required',
'Selected etype info:',
'Sending SPAKE support message',
@@ -42,9 +40,6 @@ for gnum, gname in groups:
'Continuing preauth mech PA-SPAKE (151)',
'SPAKE challenge received with group ' + str(gnum),
'Sending SPAKE response',
- '/Preauthentication failed',
- 'Encrypted timestamp ',
- 'for next request: PA-FX-COOKIE (133), PA-ENC-TIMESTAMP (2)',
'/Preauthentication failed')
realm.kinit('user', 'wrongpw', expected_code=1, expected_trace=msgs)
@@ -114,8 +109,6 @@ msgs = ('Attempting optimistic preauth',
'for next request: PA-SPAKE (151)',
'/Preauthentication failed',
'Selected etype info:',
- 'SPAKE challenge with group 1 rejected',
- 'spake (151) (real) returned: -1765328360/Preauthentication failed',
'Encrypted timestamp ',
'for next request: PA-FX-COOKIE (133), PA-ENC-TIMESTAMP (2)',
'AS key determined by preauth:',

View File

@ -0,0 +1,61 @@
From 8c2dbb9260e8beab6ae7d169e9791d8756eb40a2 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Thu, 1 Aug 2024 10:56:07 +0200
Subject: [PATCH] Set missing mask flags for kdb5_util operations
Set KADM5_TL_DATA for the use_mkey and update_princ_encryption
commands. (Commit c877f13c8985d820583b0d7ac1bb4c5dc36e677e did this
for the add_new_mkey and purge_mkeys commands.) Set appropriate flags
for the add_random_key command.
[ghudson@mit.edu: combined two commits; pruned out proposed mask flag
additions for values represented within key data or tl-data (like
KADM5_MKVNO), as those flags are currently only used in the kadm5
protocol, not to communicate with the KDB module]
ticket: 9158 (new)
(cherry picked from commit 4ed7da378940198cf4415f86d4eb013de6ac6455)
---
src/kadmin/dbutil/kdb5_mkey.c | 4 +++-
src/kadmin/dbutil/kdb5_util.c | 3 +++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/kadmin/dbutil/kdb5_mkey.c b/src/kadmin/dbutil/kdb5_mkey.c
index aceb0a9b80..ac5c51d05e 100644
--- a/src/kadmin/dbutil/kdb5_mkey.c
+++ b/src/kadmin/dbutil/kdb5_mkey.c
@@ -525,6 +525,8 @@ kdb5_use_mkey(int argc, char *argv[])
goto cleanup_return;
}
+ master_entry->mask |= KADM5_TL_DATA;
+
if ((retval = krb5_db_put_principal(util_context, master_entry))) {
com_err(progname, retval,
_("while adding master key entry to the database"));
@@ -814,7 +816,7 @@ update_princ_encryption_1(void *cb, krb5_db_entry *ent)
goto fail;
}
- ent->mask |= KADM5_KEY_DATA;
+ ent->mask |= KADM5_KEY_DATA | KADM5_TL_DATA;
if ((retval = krb5_db_put_principal(util_context, ent))) {
com_err(progname, retval, _("while updating principal '%s' key data "
diff --git a/src/kadmin/dbutil/kdb5_util.c b/src/kadmin/dbutil/kdb5_util.c
index a720eecf0b..0bb4244681 100644
--- a/src/kadmin/dbutil/kdb5_util.c
+++ b/src/kadmin/dbutil/kdb5_util.c
@@ -600,6 +600,9 @@ add_random_key(argc, argv)
exit_status++;
return;
}
+
+ dbent->mask |= KADM5_ATTRIBUTES | KADM5_KEY_DATA | KADM5_TL_DATA;
+
ret = krb5_db_put_principal(util_context, dbent);
krb5_db_free_principal(util_context, dbent);
if (ret) {
--
2.48.1

View File

@ -1,738 +0,0 @@
From 65f078dfc68f5680e87e686a59970291b64ebd95 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sun, 11 Feb 2018 15:23:35 -0500
Subject: [PATCH] Simplify kdc_preauth.c systems table
Get rid of static_preauth_systems, and replace it with explicit calls
to helper functions in get_preauth_hint_list() and return_padata().
Stop preallocating pa-data lists, instead reallocating on each
addition using add_pa_data_element(). Also simplify
maybe_add_etype_info2() using add_pa_data_element().
The KRB5_PADATA_PAC_REQUEST table entry did nothing, and was probably
originally added back when the KDC would error out on unrecognized
padata types. The KRB5_PADATA_SERVER_REFERRAL entry has been disabled
since it was first added.
(cherry picked from commit fea1a488924faa3938ef723feaa1ff12d22a91ff)
---
src/kdc/kdc_preauth.c | 526 +++++++++++++++---------------------------
1 file changed, 184 insertions(+), 342 deletions(-)
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index edc30bd83..6f34dc289 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -101,108 +101,14 @@ typedef struct preauth_system_st {
krb5_kdcpreauth_loop_fn loop;
} preauth_system;
+static preauth_system *preauth_systems;
+static size_t n_preauth_systems;
+
static krb5_error_code
make_etype_info(krb5_context context, krb5_preauthtype pa_type,
krb5_principal client, krb5_key_data *client_key,
krb5_enctype enctype, krb5_pa_data **pa_out);
-static void
-get_etype_info(krb5_context context, krb5_kdc_req *request,
- krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
- krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
- krb5_kdcpreauth_edata_respond_fn respond, void *arg);
-
-static krb5_error_code
-return_etype_info(krb5_context, krb5_pa_data *padata,
- krb5_data *req_pkt, krb5_kdc_req *request,
- krb5_kdc_rep *reply, krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa, krb5_kdcpreauth_callbacks cb,
- krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata,
- krb5_kdcpreauth_modreq modreq);
-
-static krb5_error_code
-return_pw_salt(krb5_context, krb5_pa_data *padata,
- krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply,
- krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
- krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
- krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq);
-
-
-
-static preauth_system static_preauth_systems[] = {
- {
- "FAST",
- KRB5_PADATA_FX_FAST,
- PA_HARDWARE,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- 0
- },
- {
- "etype-info",
- KRB5_PADATA_ETYPE_INFO,
- PA_HARDWARE,
- NULL,
- NULL,
- NULL,
- get_etype_info,
- 0,
- return_etype_info
- },
- {
- "etype-info2",
- KRB5_PADATA_ETYPE_INFO2,
- PA_HARDWARE,
- NULL,
- NULL,
- NULL,
- get_etype_info,
- 0,
- return_etype_info
- },
- {
- "pw-salt",
- KRB5_PADATA_PW_SALT,
- PA_PSEUDO, /* Don't include this in the error list */
- NULL,
- NULL,
- NULL,
- 0,
- 0,
- return_pw_salt
- },
- {
- "pac-request",
- KRB5_PADATA_PAC_REQUEST,
- PA_PSEUDO,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
- },
-#if 0
- {
- "server-referral",
- KRB5_PADATA_SERVER_REFERRAL,
- PA_PSEUDO,
- 0,
- 0,
- return_server_referral
- },
-#endif
-};
-
-#define NUM_STATIC_PREAUTH_SYSTEMS (sizeof(static_preauth_systems) / \
- sizeof(*static_preauth_systems))
-
-static preauth_system *preauth_systems;
-static size_t n_preauth_systems;
-
/* Get all available kdcpreauth vtables and a count of preauth types they
* support. Return an empty list on failure. */
static void
@@ -284,7 +190,6 @@ load_preauth_plugins(struct server_handle *handle, krb5_context context,
get_plugin_vtables(context, &vtables, &n_tables, &n_systems);
/* Allocate the list of static and plugin preauth systems. */
- n_systems += NUM_STATIC_PREAUTH_SYSTEMS;
preauth_systems = calloc(n_systems + 1, sizeof(preauth_system));
if (preauth_systems == NULL)
goto cleanup;
@@ -292,13 +197,8 @@ load_preauth_plugins(struct server_handle *handle, krb5_context context,
if (get_realm_names(handle, &realm_names))
goto cleanup;
- /* Add the static system to the list first. No static systems require
- * initialization, so just make a direct copy. */
- memcpy(preauth_systems, static_preauth_systems,
- sizeof(static_preauth_systems));
-
/* Add the dynamically-loaded mechanisms to the list. */
- n_systems = NUM_STATIC_PREAUTH_SYSTEMS;
+ n_systems = 0;
for (i = 0; i < n_tables; i++) {
/* Try to initialize this module. */
vt = &vtables[i];
@@ -622,7 +522,9 @@ find_pa_system(int type, preauth_system **preauth)
{
preauth_system *ap;
- ap = preauth_systems ? preauth_systems : static_preauth_systems;
+ if (preauth_systems == NULL)
+ return KRB5_PREAUTH_BAD_TYPE;
+ ap = preauth_systems;
while ((ap->type != -1) && (ap->type != type))
ap++;
if (ap->type == -1)
@@ -776,6 +678,98 @@ const char *missing_required_preauth(krb5_db_entry *client,
return 0;
}
+/* Return true if request's enctypes indicate support for etype-info2. */
+static krb5_boolean
+requires_info2(const krb5_kdc_req *request)
+{
+ int i;
+
+ for (i = 0; i < request->nktypes; i++) {
+ if (enctype_requires_etype_info_2(request->ktype[i]))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Add PA-ETYPE-INFO2 and possibly PA-ETYPE-INFO entries to pa_list as
+ * appropriate for the request and client principal. */
+static krb5_error_code
+add_etype_info(krb5_context context, krb5_kdcpreauth_rock rock,
+ krb5_pa_data ***pa_list)
+{
+ krb5_error_code ret;
+ krb5_pa_data *pa;
+
+ if (rock->client_key == NULL)
+ return 0;
+
+ if (!requires_info2(rock->request)) {
+ /* Include PA-ETYPE-INFO only for old clients. */
+ ret = make_etype_info(context, KRB5_PADATA_ETYPE_INFO,
+ rock->client->princ, rock->client_key,
+ rock->client_keyblock->enctype, &pa);
+ if (ret)
+ return ret;
+ /* add_pa_data_element() claims pa on success or failure. */
+ ret = add_pa_data_element(pa_list, pa);
+ if (ret)
+ return ret;
+ }
+
+ /* Always include PA-ETYPE-INFO2. */
+ ret = make_etype_info(context, KRB5_PADATA_ETYPE_INFO2,
+ rock->client->princ, rock->client_key,
+ rock->client_keyblock->enctype, &pa);
+ if (ret)
+ return ret;
+ /* add_pa_data_element() claims pa on success or failure. */
+ return add_pa_data_element(pa_list, pa);
+}
+
+/* Add PW-SALT or AFS3-SALT entries to pa_list as appropriate for the request
+ * and client principal. */
+static krb5_error_code
+add_pw_salt(krb5_context context, krb5_kdcpreauth_rock rock,
+ krb5_pa_data ***pa_list)
+{
+ krb5_error_code ret;
+ krb5_pa_data *pa;
+ krb5_data *salt = NULL;
+ krb5_int16 salttype;
+
+ /* Only include this pa-data for old clients. */
+ if (rock->client_key == NULL || requires_info2(rock->request))
+ return 0;
+
+ ret = krb5_dbe_compute_salt(context, rock->client_key,
+ rock->request->client, &salttype, &salt);
+ if (ret)
+ return 0;
+
+ if (salttype == KRB5_KDB_SALTTYPE_AFS3) {
+ ret = alloc_pa_data(KRB5_PADATA_AFS3_SALT, salt->length + 1, &pa);
+ if (ret)
+ goto cleanup;
+ memcpy(pa->contents, salt->data, salt->length);
+ pa->contents[salt->length] = '\0';
+ } else {
+ /* Steal memory from salt to make the pa-data entry. */
+ ret = alloc_pa_data(KRB5_PADATA_PW_SALT, 0, &pa);
+ if (ret)
+ goto cleanup;
+ pa->length = salt->length;
+ pa->contents = (uint8_t *)salt->data;
+ salt->data = NULL;
+ }
+
+ /* add_pa_data_element() claims pa on success or failure. */
+ ret = add_pa_data_element(pa_list, pa);
+
+cleanup:
+ krb5_free_data(context, salt);
+ return ret;
+}
+
struct hint_state {
kdc_hint_respond_fn respond;
void *arg;
@@ -787,7 +781,7 @@ struct hint_state {
int hw_only;
preauth_system *ap;
- krb5_pa_data **pa_data, **pa_cur;
+ krb5_pa_data **pa_data;
krb5_preauthtype pa_type;
};
@@ -799,7 +793,7 @@ hint_list_finish(struct hint_state *state, krb5_error_code code)
kdc_realm_t *kdc_active_realm = state->realm;
if (!code) {
- if (state->pa_data[0] == 0) {
+ if (state->pa_data == NULL) {
krb5_klog_syslog(LOG_INFO,
_("%spreauth required but hint list is empty"),
state->hw_only ? "hw" : "");
@@ -820,20 +814,27 @@ hint_list_next(struct hint_state *arg);
static void
finish_get_edata(void *arg, krb5_error_code code, krb5_pa_data *pa)
{
+ krb5_error_code ret;
struct hint_state *state = arg;
if (code == 0) {
if (pa == NULL) {
- /* Include an empty value of the current type. */
- pa = calloc(1, sizeof(*pa));
- pa->magic = KV5M_PA_DATA;
- pa->pa_type = state->pa_type;
+ ret = alloc_pa_data(state->pa_type, 0, &pa);
+ if (ret)
+ goto error;
}
- *state->pa_cur++ = pa;
+ /* add_pa_data_element() claims pa on success or failure. */
+ ret = add_pa_data_element(&state->pa_data, pa);
+ if (ret)
+ goto error;
}
state->ap++;
hint_list_next(state);
+ return;
+
+error:
+ hint_list_finish(state, ret);
}
static void
@@ -870,16 +871,16 @@ get_preauth_hint_list(krb5_kdc_req *request, krb5_kdcpreauth_rock rock,
krb5_pa_data ***e_data_out, kdc_hint_respond_fn respond,
void *arg)
{
+ kdc_realm_t *kdc_active_realm = rock->rstate->realm_data;
struct hint_state *state;
+ krb5_pa_data *pa;
*e_data_out = NULL;
/* Allocate our state. */
state = calloc(1, sizeof(*state));
- if (state == NULL) {
- (*respond)(arg);
- return;
- }
+ if (state == NULL)
+ goto error;
state->hw_only = isflagset(rock->client->attributes,
KRB5_KDB_REQUIRES_HW_AUTH);
state->respond = respond;
@@ -888,17 +889,27 @@ get_preauth_hint_list(krb5_kdc_req *request, krb5_kdcpreauth_rock rock,
state->rock = rock;
state->realm = rock->rstate->realm_data;
state->e_data_out = e_data_out;
-
- state->pa_data = calloc(n_preauth_systems + 1, sizeof(krb5_pa_data *));
- if (!state->pa_data) {
- free(state);
- (*respond)(arg);
- return;
- }
-
- state->pa_cur = state->pa_data;
+ state->pa_data = NULL;
state->ap = preauth_systems;
+
+ /* Add an empty PA-FX-FAST element to advertise FAST support. */
+ if (alloc_pa_data(KRB5_PADATA_FX_FAST, 0, &pa) != 0)
+ goto error;
+ /* add_pa_data_element() claims pa on success or failure. */
+ if (add_pa_data_element(&state->pa_data, pa) != 0)
+ goto error;
+
+ if (add_etype_info(kdc_context, rock, &state->pa_data) != 0)
+ goto error;
+
hint_list_next(state);
+ return;
+
+error:
+ if (state != NULL)
+ krb5_free_pa_data(kdc_context, state->pa_data);
+ free(state);
+ (*respond)(arg);
}
/*
@@ -1029,10 +1040,10 @@ filter_preauth_error(krb5_error_code code)
static krb5_error_code
maybe_add_etype_info2(struct padata_state *state, krb5_error_code code)
{
+ krb5_error_code ret;
krb5_context context = state->context;
krb5_kdcpreauth_rock rock = state->rock;
- krb5_pa_data **list = state->pa_e_data;
- size_t count;
+ krb5_pa_data *pa;
/* Only add key information when requesting another preauth round trip. */
if (code != KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
@@ -1048,18 +1059,14 @@ maybe_add_etype_info2(struct padata_state *state, krb5_error_code code)
KRB5_PADATA_FX_COOKIE) != NULL)
return 0;
- /* Reallocate state->pa_e_data to make room for the etype-info2 element. */
- for (count = 0; list != NULL && list[count] != NULL; count++);
- list = realloc(list, (count + 2) * sizeof(*list));
- if (list == NULL)
- return ENOMEM;
- list[count] = list[count + 1] = NULL;
- state->pa_e_data = list;
+ ret = make_etype_info(context, KRB5_PADATA_ETYPE_INFO2,
+ rock->client->princ, rock->client_key,
+ rock->client_keyblock->enctype, &pa);
+ if (ret)
+ return ret;
- /* Generate an etype-info2 element in the new slot. */
- return make_etype_info(context, KRB5_PADATA_ETYPE_INFO2,
- rock->client->princ, rock->client_key,
- rock->client_keyblock->enctype, &list[count]);
+ /* add_pa_data_element() claims pa on success or failure. */
+ return add_pa_data_element(&state->pa_e_data, pa);
}
/* Release state and respond to the AS-REQ processing code with the result of
@@ -1279,17 +1286,20 @@ return_padata(krb5_context context, krb5_kdcpreauth_rock rock,
{
krb5_error_code retval;
krb5_pa_data ** padata;
- krb5_pa_data ** send_pa_list;
- krb5_pa_data ** send_pa;
+ krb5_pa_data ** send_pa_list = NULL;
+ krb5_pa_data * send_pa;
krb5_pa_data * pa = 0;
krb5_pa_data null_item;
preauth_system * ap;
- int * pa_order;
+ int * pa_order = NULL;
int * pa_type;
int size = 0;
krb5_kdcpreauth_modreq *modreq_ptr;
krb5_boolean key_modified;
krb5_keyblock original_key;
+
+ memset(&original_key, 0, sizeof(original_key));
+
if ((!*padata_context) &&
(make_padata_context(context, padata_context) != 0)) {
return KRB5KRB_ERR_GENERIC;
@@ -1300,26 +1310,18 @@ return_padata(krb5_context context, krb5_kdcpreauth_rock rock,
size++;
}
- if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
- return ENOMEM;
- if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) {
- free(send_pa_list);
- return ENOMEM;
- }
+ pa_order = k5calloc(size + 1, sizeof(int), &retval);
+ if (pa_order == NULL)
+ goto cleanup;
sort_pa_order(context, request, pa_order);
retval = krb5_copy_keyblock_contents(context, encrypting_key,
&original_key);
- if (retval) {
- free(send_pa_list);
- free(pa_order);
- return retval;
- }
+ if (retval)
+ goto cleanup;
key_modified = FALSE;
null_item.contents = NULL;
null_item.length = 0;
- send_pa = send_pa_list;
- *send_pa = 0;
for (pa_type = pa_order; *pa_type != -1; pa_type++) {
ap = &preauth_systems[*pa_type];
@@ -1349,20 +1351,30 @@ return_padata(krb5_context context, krb5_kdcpreauth_rock rock,
}
}
}
+ send_pa = NULL;
retval = ap->return_padata(context, pa, req_pkt, request, reply,
- encrypting_key, send_pa, &callbacks, rock,
+ encrypting_key, &send_pa, &callbacks, rock,
ap->moddata, *modreq_ptr);
if (retval)
goto cleanup;
- if (*send_pa)
- send_pa++;
- *send_pa = 0;
+ if (send_pa != NULL) {
+ /* add_pa_data_element() claims send_pa on success or failure. */
+ retval = add_pa_data_element(&send_pa_list, send_pa);
+ if (retval)
+ goto cleanup;
+ }
}
- retval = 0;
+ /* Add etype-info and pw-salt pa-data as needed. */
+ retval = add_etype_info(context, rock, &send_pa_list);
+ if (retval)
+ goto cleanup;
+ retval = add_pw_salt(context, rock, &send_pa_list);
+ if (retval)
+ goto cleanup;
- if (send_pa_list[0]) {
+ if (send_pa_list != NULL) {
reply->padata = send_pa_list;
send_pa_list = 0;
}
@@ -1370,8 +1382,7 @@ return_padata(krb5_context context, krb5_kdcpreauth_rock rock,
cleanup:
krb5_free_keyblock_contents(context, &original_key);
free(pa_order);
- if (send_pa_list)
- krb5_free_pa_data(context, send_pa_list);
+ krb5_free_pa_data(context, send_pa_list);
return (retval);
}
@@ -1438,9 +1449,8 @@ make_etype_info(krb5_context context, krb5_preauthtype pa_type,
krb5_enctype enctype, krb5_pa_data **pa_out)
{
krb5_error_code retval;
- krb5_pa_data *pa = NULL;
krb5_etype_info_entry **entry = NULL;
- krb5_data *scratch = NULL;
+ krb5_data *der_etype_info = NULL;
int etype_info2 = (pa_type == KRB5_PADATA_ETYPE_INFO2);
*pa_out = NULL;
@@ -1454,125 +1464,23 @@ make_etype_info(krb5_context context, krb5_preauthtype pa_type,
goto cleanup;
if (etype_info2)
- retval = encode_krb5_etype_info2(entry, &scratch);
+ retval = encode_krb5_etype_info2(entry, &der_etype_info);
else
- retval = encode_krb5_etype_info(entry, &scratch);
+ retval = encode_krb5_etype_info(entry, &der_etype_info);
if (retval)
goto cleanup;
- pa = k5alloc(sizeof(*pa), &retval);
- if (pa == NULL)
+
+ /* Steal the data from der_etype_info to create a pa-data element. */
+ retval = alloc_pa_data(pa_type, 0, pa_out);
+ if (retval)
goto cleanup;
- pa->magic = KV5M_PA_DATA;
- pa->pa_type = pa_type;
- pa->contents = (unsigned char *)scratch->data;
- pa->length = scratch->length;
- scratch->data = NULL;
- *pa_out = pa;
+ (*pa_out)->contents = (uint8_t *)der_etype_info->data;
+ (*pa_out)->length = der_etype_info->length;
+ der_etype_info->data = NULL;
cleanup:
krb5_free_etype_info(context, entry);
- krb5_free_data(context, scratch);
- return retval;
-}
-
-/* Return true if request's enctypes indicate support for etype-info2. */
-static krb5_boolean
-requires_info2(const krb5_kdc_req *request)
-{
- int i;
-
- for (i = 0; i < request->nktypes; i++) {
- if (enctype_requires_etype_info_2(request->ktype[i]))
- return TRUE;
- }
- return FALSE;
-}
-
-/* Generate hint list padata for PA-ETYPE-INFO or PA-ETYPE-INFO2. */
-static void
-get_etype_info(krb5_context context, krb5_kdc_req *request,
- krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
- krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
- krb5_kdcpreauth_edata_respond_fn respond, void *arg)
-{
- krb5_error_code ret;
- krb5_pa_data *pa = NULL;
-
- if (rock->client_key == NULL) {
- ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
- } else if (pa_type == KRB5_PADATA_ETYPE_INFO && requires_info2(request)) {
- ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
- } else {
- ret = make_etype_info(context, pa_type, rock->client->princ,
- rock->client_key, rock->client_keyblock->enctype,
- &pa);
- }
- (*respond)(arg, ret, pa);
-}
-
-/* Generate AS-REP padata for PA-ETYPE-INFO or PA-ETYPE-INFO2. */
-static krb5_error_code
-return_etype_info(krb5_context context, krb5_pa_data *padata,
- krb5_data *req_pkt, krb5_kdc_req *request,
- krb5_kdc_rep *reply, krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa, krb5_kdcpreauth_callbacks cb,
- krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata,
- krb5_kdcpreauth_modreq modreq)
-{
- *send_pa = NULL;
- if (rock->client_key == NULL)
- return 0;
- if (padata->pa_type == KRB5_PADATA_ETYPE_INFO && requires_info2(request))
- return 0;
- return make_etype_info(context, padata->pa_type, rock->client->princ,
- rock->client_key, encrypting_key->enctype, send_pa);
-}
-
-static krb5_error_code
-return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
- krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply,
- krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
- krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
- krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq)
-{
- krb5_error_code retval;
- krb5_pa_data * padata;
- krb5_data * salt = NULL;
- krb5_int16 salttype;
- krb5_key_data * client_key = rock->client_key;
-
- if (client_key == NULL || requires_info2(request))
- return 0;
-
- retval = krb5_dbe_compute_salt(context, client_key, request->client,
- &salttype, &salt);
- if (retval)
- return 0;
-
- padata = k5alloc(sizeof(*padata), &retval);
- if (padata == NULL)
- goto cleanup;
- padata->magic = KV5M_PA_DATA;
-
- if (salttype == KRB5_KDB_SALTTYPE_AFS3) {
- padata->contents = k5memdup0(salt->data, salt->length, &retval);
- if (padata->contents == NULL)
- goto cleanup;
- padata->pa_type = KRB5_PADATA_AFS3_SALT;
- padata->length = salt->length + 1;
- } else {
- padata->pa_type = KRB5_PADATA_PW_SALT;
- padata->length = salt->length;
- padata->contents = (krb5_octet *)salt->data;
- salt->data = NULL;
- }
-
- *send_pa = padata;
- padata = NULL;
-
-cleanup:
- free(padata);
- krb5_free_data(context, salt);
+ krb5_free_data(context, der_etype_info);
return retval;
}
@@ -1656,69 +1564,3 @@ return_enc_padata(krb5_context context, krb5_data *req_pkt,
cleanup:
return code;
}
-
-
-#if 0
-static krb5_error_code return_server_referral(krb5_context context,
- krb5_pa_data * padata,
- krb5_db_entry *client,
- krb5_db_entry *server,
- krb5_kdc_req *request,
- krb5_kdc_rep *reply,
- krb5_key_data *client_key,
- krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa)
-{
- krb5_error_code code;
- krb5_tl_data tl_data;
- krb5_pa_data *pa_data;
- krb5_enc_data enc_data;
- krb5_data plain;
- krb5_data *enc_pa_data;
-
- *send_pa = NULL;
-
- tl_data.tl_data_type = KRB5_TL_SERVER_REFERRAL;
-
- code = krb5_dbe_lookup_tl_data(context, server, &tl_data);
- if (code || tl_data.tl_data_length == 0)
- return 0; /* no server referrals to return */
-
- plain.length = tl_data.tl_data_length;
- plain.data = tl_data.tl_data_contents;
-
- /* Encrypt ServerReferralData */
- code = krb5_encrypt_helper(context, encrypting_key,
- KRB5_KEYUSAGE_PA_SERVER_REFERRAL_DATA,
- &plain, &enc_data);
- if (code)
- return code;
-
- /* Encode ServerReferralData into PA-SERVER-REFERRAL-DATA */
- code = encode_krb5_enc_data(&enc_data, &enc_pa_data);
- if (code) {
- krb5_free_data_contents(context, &enc_data.ciphertext);
- return code;
- }
-
- krb5_free_data_contents(context, &enc_data.ciphertext);
-
- /* Return PA-SERVER-REFERRAL-DATA */
- pa_data = (krb5_pa_data *)malloc(sizeof(*pa_data));
- if (pa_data == NULL) {
- krb5_free_data(context, enc_pa_data);
- return ENOMEM;
- }
-
- pa_data->magic = KV5M_PA_DATA;
- pa_data->pa_type = KRB5_PADATA_SVR_REFERRAL_INFO;
- pa_data->length = enc_pa_data->length;
- pa_data->contents = enc_pa_data->data;
-
- free(enc_pa_data); /* don't free contents */
-
- *send_pa = pa_data;
-
- return 0;
-}
-#endif

View File

@ -0,0 +1,91 @@
From 6b4cdaac48e6b736b66ccc21f4eed7c6fc4c2e4a Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 4 Mar 2022 00:45:00 -0500
Subject: [PATCH] Try harder to avoid password change replay errors
Commit d7b3018d338fc9c989c3fa17505870f23c3759a8 (ticket 7905) changed
change_set_password() to prefer TCP. However, because UDP_LAST falls
back to UDP after one second, we can still get a replay error due to a
dropped packet, before the TCP layer has a chance to retry.
Instead, try k5_sendto() with NO_UDP, and only fall back to UDP after
TCP fails completely without reaching a server. In sendto_kdc.c,
implement an ONLY_UDP transport strategy to allow the UDP fallback.
ticket: 9037
---
src/lib/krb5/os/changepw.c | 9 ++++++++-
src/lib/krb5/os/os-proto.h | 1 +
src/lib/krb5/os/sendto_kdc.c | 12 ++++++++----
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c
index 9f968da7f..c59232586 100644
--- a/src/lib/krb5/os/changepw.c
+++ b/src/lib/krb5/os/changepw.c
@@ -255,9 +255,16 @@ change_set_password(krb5_context context,
callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
krb5_free_data_contents(callback_ctx.context, &chpw_rep);
+ /* UDP retransmits may be seen as replays. Only try UDP after other
+ * transports fail completely. */
code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
- &sl, UDP_LAST, &callback_info, &chpw_rep,
+ &sl, NO_UDP, &callback_info, &chpw_rep,
ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
+ if (code == KRB5_KDC_UNREACH) {
+ code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
+ &sl, ONLY_UDP, &callback_info, &chpw_rep,
+ ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
+ }
if (code)
goto cleanup;
diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
index a16a34b74..ad3839131 100644
--- a/src/lib/krb5/os/os-proto.h
+++ b/src/lib/krb5/os/os-proto.h
@@ -49,6 +49,7 @@ typedef enum {
UDP_FIRST = 0,
UDP_LAST,
NO_UDP,
+ ONLY_UDP
} k5_transport_strategy;
/* A single server hostname or address. */
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 82523c561..d76e24ccf 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -799,11 +799,14 @@ resolve_server(krb5_context context, const krb5_data *realm,
int err, result;
char portbuf[PORT_LENGTH];
- /* Skip UDP entries if we don't want UDP. */
+ /* Skip entries excluded by the strategy. */
if (strategy == NO_UDP && entry->transport == UDP)
return 0;
+ if (strategy == ONLY_UDP && entry->transport != UDP &&
+ entry->transport != TCP_OR_UDP)
+ return 0;
- transport = (strategy == UDP_FIRST) ? UDP : TCP;
+ transport = (strategy == UDP_FIRST || strategy == ONLY_UDP) ? UDP : TCP;
if (entry->hostname == NULL) {
/* Added by a module, so transport is either TCP or UDP. */
ai.ai_socktype = socktype_for_transport(entry->transport);
@@ -847,8 +850,9 @@ resolve_server(krb5_context context, const krb5_data *realm,
}
/* For TCP_OR_UDP entries, add each address again with the non-preferred
- * transport, unless we are avoiding UDP. Flag these as deferred. */
- if (retval == 0 && entry->transport == TCP_OR_UDP && strategy != NO_UDP) {
+ * transport, if there is one. Flag these as deferred. */
+ if (retval == 0 && entry->transport == TCP_OR_UDP &&
+ (strategy == UDP_FIRST || strategy == UDP_LAST)) {
transport = (strategy == UDP_FIRST) ? TCP : UDP;
for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
a->ai_socktype = socktype_for_transport(transport);
--
2.35.1

View File

@ -0,0 +1,186 @@
From 6858ecbb9c407ff6d2b22cac283ea2461af1757b Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 20 Aug 2020 17:49:29 -0400
Subject: [PATCH] Unify kvno option documentation
Add missing kvno options to the kvno.rst synopsis and option
descriptions, and to the kvno usage message. Remove mention of '-h'
(help text), from kvno.rst as it is an implicit option. Note that the
three new caching options were added in release 1.19.
Indicate the two exclusions (-u/-S and --u2u with the S4U2Self options)
and dependency (-P on S4U2Self) where they are missing.
Switch xusage() to print only a single localized string, rather than
running each line of output through localization separately.
Leave kvno -C undocumented for now, as the semantics of
KRB5_GC_CANONICALIZE are minimally useful and likely to change.
[ghudson@mit.edu: edited documentation and commit message]
ticket: 7476
tags: pullup
target_version: 1.18-next
(cherry picked from commit becd1ad6830b526d08ddaf5b2b6f213154c6446c)
(cherry picked from commit 52e3695cc5ef00766e12adfe8ed276c2885e71bb)
---
doc/user/user_commands/kvno.rst | 24 +++++++++++++-----------
src/clients/kvno/kvno.c | 15 +++++++++------
src/man/kvno.man | 24 +++++++++++++-----------
3 files changed, 35 insertions(+), 28 deletions(-)
diff --git a/doc/user/user_commands/kvno.rst b/doc/user/user_commands/kvno.rst
index 718313576..65c44e1c0 100644
--- a/doc/user/user_commands/kvno.rst
+++ b/doc/user/user_commands/kvno.rst
@@ -10,13 +10,9 @@ SYNOPSIS
[**-c** *ccache*]
[**-e** *etype*]
[**-q**]
-[**-h**]
+[**-u** | **-S** *sname*]
[**-P**]
-[**-S** *sname*]
-[**-I** *for_user*]
-[**-U** *for_user*]
-[**-F** *cert_file*]
-[**--u2u** *ccache*]
+[[{**-F** *cert_file* | {**-I** | **-U**} *for_user*} [**-P**]] | **--u2u** *ccache*]
*service1 service2* ...
@@ -39,13 +35,18 @@ OPTIONS
of all the services named on the command line. This is useful in
certain backward compatibility situations.
+**-k** *keytab*
+ Decrypt the acquired tickets using *keytab* to confirm their
+ validity.
+
**-q**
Suppress printing output when successful. If a service ticket
cannot be obtained, an error message will still be printed and
kvno will exit with nonzero status.
-**-h**
- Prints a usage statement and exits.
+**-u**
+ Use the unknown name type in requested service principal names.
+ This option Cannot be used with *-S*.
**-P**
Specifies that the *service1 service2* ... arguments are to be
@@ -76,16 +77,17 @@ OPTIONS
**--cached-only**
Only retrieve credentials already present in the cache, not from
- the KDC.
+ the KDC. (Added in release 1.19.)
**--no-store**
Do not store retrieved credentials in the cache. If
**--out-cache** is also specified, credentials will still be
- stored into the output credential cache.
+ stored into the output credential cache. (Added in release 1.19.)
**--out-cache** *ccache*
Initialize *ccache* and store all retrieved credentials into it.
- Do not store acquired credentials in the input cache.
+ Do not store acquired credentials in the input cache. (Added in
+ release 1.19.)
**--u2u** *ccache*
Requests a user-to-user ticket. *ccache* must contain a local
diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c
index 9d85864f6..c5f6bf700 100644
--- a/src/clients/kvno/kvno.c
+++ b/src/clients/kvno/kvno.c
@@ -38,15 +38,18 @@
static char *prog;
static int quiet = 0;
+#define XUSAGE_BREAK "\n\t"
+
static void
xusage()
{
- fprintf(stderr, _("usage: %s [-C] [-u] [-c ccache] [-e etype]\n"), prog);
- fprintf(stderr, _("\t[-k keytab] [-S sname] [{-I | -U} for_user | "
- "[-F cert_file] [-P]]\n"));
- fprintf(stderr, _("\t[--cached-only] [--no-store] [--out-cache ccache] "
- "[--u2u ccache]\n"));
- fprintf(stderr, _("\tservice1 service2 ...\n"));
+ fprintf(stderr, _("usage: %s [-c ccache] [-e etype] [-k keytab] [-q] "
+ "[-u | -S sname]" XUSAGE_BREAK
+ "[[{-F cert_file | {-I | -U} for_user} [-P]] | "
+ "--u2u ccache]" XUSAGE_BREAK
+ "[--cached-only] [--no-store] [--out-cache] "
+ "service1 service2 ...\n"),
+ prog);
exit(1);
}
diff --git a/src/man/kvno.man b/src/man/kvno.man
index b9f6739eb..22318324d 100644
--- a/src/man/kvno.man
+++ b/src/man/kvno.man
@@ -36,13 +36,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
[\fB\-c\fP \fIccache\fP]
[\fB\-e\fP \fIetype\fP]
[\fB\-q\fP]
-[\fB\-h\fP]
+[\fB\-u\fP | \fB\-S\fP \fIsname\fP]
[\fB\-P\fP]
-[\fB\-S\fP \fIsname\fP]
-[\fB\-I\fP \fIfor_user\fP]
-[\fB\-U\fP \fIfor_user\fP]
-[\fB\-F\fP \fIcert_file\fP]
-[\fB\-\-u2u\fP \fIccache\fP]
+[[{\fB\-F\fP \fIcert_file\fP | {\fB\-I\fP | \fB\-U\fP} \fIfor_user\fP} [\fB\-P\fP]] | \fB\-\-u2u\fP \fIccache\fP]
\fIservice1 service2\fP ...
.SH DESCRIPTION
.sp
@@ -60,13 +56,18 @@ Specifies the enctype which will be requested for the session key
of all the services named on the command line. This is useful in
certain backward compatibility situations.
.TP
+\fB\-k\fP \fIkeytab\fP
+Decrypt the acquired tickets using \fIkeytab\fP to confirm their
+validity.
+.TP
\fB\-q\fP
Suppress printing output when successful. If a service ticket
cannot be obtained, an error message will still be printed and
kvno will exit with nonzero status.
.TP
-\fB\-h\fP
-Prints a usage statement and exits.
+\fB\-u\fP
+Use the unknown name type in requested service principal names.
+This option Cannot be used with \fI\-S\fP\&.
.TP
\fB\-P\fP
Specifies that the \fIservice1 service2\fP ... arguments are to be
@@ -97,16 +98,17 @@ certificate file must be in PEM format.
.TP
\fB\-\-cached\-only\fP
Only retrieve credentials already present in the cache, not from
-the KDC.
+the KDC. (Added in release 1.19.)
.TP
\fB\-\-no\-store\fP
Do not store retrieved credentials in the cache. If
\fB\-\-out\-cache\fP is also specified, credentials will still be
-stored into the output credential cache.
+stored into the output credential cache. (Added in release 1.19.)
.TP
\fB\-\-out\-cache\fP \fIccache\fP
Initialize \fIccache\fP and store all retrieved credentials into it.
-Do not store acquired credentials in the input cache.
+Do not store acquired credentials in the input cache. (Added in
+release 1.19.)
.TP
\fB\-\-u2u\fP \fIccache\fP
Requests a user\-to\-user ticket. \fIccache\fP must contain a local

View File

@ -0,0 +1,184 @@
From b6ada496a285a7b350e28c97b53b6f659a9a94b9 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sat, 11 Dec 2021 01:25:34 -0500
Subject: [PATCH] Use 14 instead of 9 for unkeyed SHA-1 checksum
Although MIT krb5 had been using the value 9 for unkeyed SHA-1 since
its 1.0 release in 1996, RFC 3961 instead assigned this value to
rsa-md5-des3 (likely never used), and assigned the values 10 and 14 to
SHA-1. Heimdal and Microsoft use the value 14. Unkeyed SHA-1 almost
never appears on the wire, but has been seen in PKINIT asChecksum
fields in replies from Windows KDCs (despite the field being specified
as a keyed checksum).
Define a new symbol CKSUMTYPE_SHA1 with the value 14, and use it where
we currently use CKSUMTYPE_NIST_SHA. Continue to allow the value 9
for ABI compatibility. Remove the pkinit_clnt.c workaround as the
value 14 will now work without adjustment.
ticket: 9040 (new)
---
doc/appdev/refs/macros/index.rst | 1 +
src/include/krb5/krb5.hin | 6 ++++++
src/lib/crypto/crypto_tests/t_cksums.c | 2 +-
src/lib/crypto/krb/cksumtypes.c | 6 ++++++
src/lib/gssapi/mechglue/g_saslname.c | 3 +--
src/lib/krb5/os/trace.c | 2 +-
src/plugins/kdb/test/kdb_test.c | 2 +-
src/plugins/preauth/pkinit/pkinit_clnt.c | 11 ++---------
src/plugins/preauth/pkinit/pkinit_srv.c | 4 ++--
9 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index 788d094bff..001fb386a7 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -42,6 +42,7 @@ Public
CKSUMTYPE_RSA_MD4_DES.rst
CKSUMTYPE_RSA_MD5.rst
CKSUMTYPE_RSA_MD5_DES.rst
+ CKSUMTYPE_SHA1.rst
ENCTYPE_AES128_CTS_HMAC_SHA1_96.rst
ENCTYPE_AES128_CTS_HMAC_SHA256_128.rst
ENCTYPE_AES256_CTS_HMAC_SHA1_96.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index d2cf1eba2a..a7060aa733 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -449,6 +449,11 @@ typedef struct _krb5_crypto_iov {
#define ENCTYPE_CAMELLIA256_CTS_CMAC 0x001a /**< RFC 6803 */
#define ENCTYPE_UNKNOWN 0x01ff
+/*
+ * Historically we used the value 9 for unkeyed SHA-1. RFC 3961 assigns this
+ * value to rsa-md5-des3, which fortunately is unused. For ABI compatibility
+ * we allow either 9 or 14 for SHA-1.
+ */
#define CKSUMTYPE_CRC32 0x0001
#define CKSUMTYPE_RSA_MD4 0x0002
#define CKSUMTYPE_RSA_MD4_DES 0x0003
@@ -459,6 +464,7 @@ typedef struct _krb5_crypto_iov {
#define CKSUMTYPE_RSA_MD5_DES 0x0008
#define CKSUMTYPE_NIST_SHA 0x0009
#define CKSUMTYPE_HMAC_SHA1_DES3 0x000c /* @deprecated removed */
+#define CKSUMTYPE_SHA1 0x000d /**< RFC 3962 */
#define CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f /**< RFC 3962. Used with
ENCTYPE_AES128_CTS_HMAC_SHA1_96 */
#define CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010 /**< RFC 3962. Used with
diff --git a/src/lib/crypto/crypto_tests/t_cksums.c b/src/lib/crypto/crypto_tests/t_cksums.c
index 84408fb68a..de5ed3a22b 100644
--- a/src/lib/crypto/crypto_tests/t_cksums.c
+++ b/src/lib/crypto/crypto_tests/t_cksums.c
@@ -54,7 +54,7 @@ struct test {
},
{
{ KV5M_DATA, 0, "" },
- CKSUMTYPE_NIST_SHA, 0, 0, { KV5M_DATA, 0, "" },
+ CKSUMTYPE_SHA1, 0, 0, { KV5M_DATA, 0, "" },
{ KV5M_DATA, 20,
"\xDA\x39\xA3\xEE\x5E\x6B\x4B\x0D\x32\x55\xBF\xEF\x95\x60\x18\x90"
"\xAF\xD8\x07\x09" }
diff --git a/src/lib/crypto/krb/cksumtypes.c b/src/lib/crypto/krb/cksumtypes.c
index f5fbe8a2a7..25a3ffd2d2 100644
--- a/src/lib/crypto/krb/cksumtypes.c
+++ b/src/lib/crypto/krb/cksumtypes.c
@@ -46,6 +46,12 @@ const struct krb5_cksumtypes krb5int_cksumtypes_list[] = {
krb5int_unkeyed_checksum, NULL,
20, 20, CKSUM_UNKEYED },
+ { CKSUMTYPE_SHA1,
+ "sha", { 0 }, "SHA-1",
+ NULL, &krb5int_hash_sha1,
+ krb5int_unkeyed_checksum, NULL,
+ 20, 20, CKSUM_UNKEYED },
+
{ CKSUMTYPE_HMAC_MD5_ARCFOUR,
"hmac-md5-rc4", { "hmac-md5-enc", "hmac-md5-earcfour" },
"Microsoft HMAC MD5",
diff --git a/src/lib/gssapi/mechglue/g_saslname.c b/src/lib/gssapi/mechglue/g_saslname.c
index e25f9e0a53..2be0c8a69a 100644
--- a/src/lib/gssapi/mechglue/g_saslname.c
+++ b/src/lib/gssapi/mechglue/g_saslname.c
@@ -58,8 +58,7 @@ oidToSaslName(OM_uint32 *minor, const gss_OID mech,
iov[2].data.length = sizeof(cksumBuf);
iov[2].data.data = (char *)cksumBuf;
- *minor = krb5_k_make_checksum_iov(NULL, CKSUMTYPE_NIST_SHA,
- NULL, 0, iov, 3);
+ *minor = krb5_k_make_checksum_iov(NULL, CKSUMTYPE_SHA1, NULL, 0, iov, 3);
if (*minor != 0)
return GSS_S_FAILURE;
diff --git a/src/lib/krb5/os/trace.c b/src/lib/krb5/os/trace.c
index e9b99f4ca0..abb8a3f21b 100644
--- a/src/lib/krb5/os/trace.c
+++ b/src/lib/krb5/os/trace.c
@@ -93,7 +93,7 @@ hash_bytes(krb5_context context, const void *ptr, size_t len)
krb5_data d = make_data((void *) ptr, len);
char *s = NULL;
- if (krb5_k_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, &d,
+ if (krb5_k_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, &d,
&cksum) != 0)
return NULL;
if (cksum.length >= 2)
diff --git a/src/plugins/kdb/test/kdb_test.c b/src/plugins/kdb/test/kdb_test.c
index 95a6062e2a..38d371cb86 100644
--- a/src/plugins/kdb/test/kdb_test.c
+++ b/src/plugins/kdb/test/kdb_test.c
@@ -205,7 +205,7 @@ make_keyblock(krb5_kvno kvno, krb5_enctype etype, int32_t salttype,
(int)salttype, princstr, (int)realm->length, realm->data) < 0)
abort();
d = string2data(hashstr);
- check(krb5_c_make_checksum(NULL, CKSUMTYPE_NIST_SHA, NULL, 0, &d, &cksum));
+ check(krb5_c_make_checksum(NULL, CKSUMTYPE_SHA1, NULL, 0, &d, &cksum));
/* Make the appropriate number of input bytes from the hash result. */
for (pos = 0; pos < keybytes; pos += n) {
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index 9b991ffe05..021e5f0723 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -119,8 +119,8 @@ pa_pkinit_gen_req(krb5_context context,
goto cleanup;
}
- retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0,
- der_req, &cksum);
+ retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req,
+ &cksum);
if (retval)
goto cleanup;
TRACE_PKINIT_CLIENT_REQ_CHECKSUM(context, &cksum);
@@ -701,13 +701,6 @@ pkinit_as_rep_parse(krb5_context context,
pkiDebug("failed to decode reply_key_pack\n");
goto cleanup;
}
- /*
- * This is hack but Windows sends back SHA1 checksum
- * with checksum type of 14. There is currently no
- * checksum type of 14 defined.
- */
- if (key_pack->asChecksum.checksum_type == 14)
- key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA;
retval = krb5_c_make_checksum(context,
key_pack->asChecksum.checksum_type,
&key_pack->replyKey,
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
index 3ae56c0641..3bff456f8f 100644
--- a/src/plugins/preauth/pkinit/pkinit_srv.c
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -546,8 +546,8 @@ pkinit_server_verify_padata(krb5_context context,
goto cleanup;
}
der_req = cb->request_body(context, rock);
- retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0,
- der_req, &cksum);
+ retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req,
+ &cksum);
if (retval) {
pkiDebug("unable to calculate AS REQ checksum\n");
goto cleanup;
--
2.39.2

View File

@ -0,0 +1,237 @@
From 00a2ccfeaeac7a0019a73a97cfe33063ba90c7f3 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 26 Mar 2021 23:38:54 -0400
Subject: [PATCH] Use KCM_OP_RETRIEVE in KCM client
In kcm_retrieve(), try KCM_OP_RETRIEVE. Fall back to iteration if the
server doesn't implement it, or if we can an answer incompatible with
KRB5_TC_SUPPORTED_KTYPES.
In kcmserver.py, implement partial decoding for creds and cred tags so
that we can do a basic principal name match.
ticket: 8997 (new)
(cherry picked from commit 795ebba8c039be172ab93cd41105c73ffdba0fdb)
(cherry picked from commit c56d4b87de0f30a38dc61d374ad225d02d581eb3)
(cherry picked from commit ac0a117096324fa73afae291ed467f2ea66e279b)
---
src/include/kcm.h | 2 +-
src/lib/krb5/ccache/cc_kcm.c | 52 +++++++++++++++++++++++++++++++++---
src/tests/kcmserver.py | 44 +++++++++++++++++++++++++++---
src/tests/t_ccache.py | 11 +++++---
4 files changed, 99 insertions(+), 10 deletions(-)
diff --git a/src/include/kcm.h b/src/include/kcm.h
index 9b66f1cbd..85c20d345 100644
--- a/src/include/kcm.h
+++ b/src/include/kcm.h
@@ -87,7 +87,7 @@ typedef enum kcm_opcode {
KCM_OP_INITIALIZE, /* (name, princ) -> () */
KCM_OP_DESTROY, /* (name) -> () */
KCM_OP_STORE, /* (name, cred) -> () */
- KCM_OP_RETRIEVE,
+ KCM_OP_RETRIEVE, /* (name, flags, credtag) -> (cred) */
KCM_OP_GET_PRINCIPAL, /* (name) -> (princ) */
KCM_OP_GET_CRED_UUID_LIST, /* (name) -> (uuid, ...) */
KCM_OP_GET_CRED_BY_UUID, /* (name, uuid) -> (cred) */
diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c
index dae622feb..b600c6f15 100644
--- a/src/lib/krb5/ccache/cc_kcm.c
+++ b/src/lib/krb5/ccache/cc_kcm.c
@@ -826,9 +826,55 @@ static krb5_error_code KRB5_CALLCONV
kcm_retrieve(krb5_context context, krb5_ccache cache, krb5_flags flags,
krb5_creds *mcred, krb5_creds *cred_out)
{
- /* There is a KCM opcode for retrieving creds, but Heimdal's client doesn't
- * use it. It causes the KCM daemon to actually make a TGS request. */
- return k5_cc_retrieve_cred_default(context, cache, flags, mcred, cred_out);
+ krb5_error_code ret;
+ struct kcmreq req = EMPTY_KCMREQ;
+ krb5_creds cred;
+ krb5_enctype *enctypes = NULL;
+
+ memset(&cred, 0, sizeof(cred));
+
+ /* Include KCM_GC_CACHED in flags to prevent Heimdal's sssd from making a
+ * TGS request itself. */
+ kcmreq_init(&req, KCM_OP_RETRIEVE, cache);
+ k5_buf_add_uint32_be(&req.reqbuf, map_tcflags(flags) | KCM_GC_CACHED);
+ k5_marshal_mcred(&req.reqbuf, mcred);
+ ret = cache_call(context, cache, &req);
+
+ /* Fall back to iteration if the server does not support retrieval. */
+ if (ret == KRB5_FCC_INTERNAL || ret == KRB5_CC_IO) {
+ ret = k5_cc_retrieve_cred_default(context, cache, flags, mcred,
+ cred_out);
+ goto cleanup;
+ }
+ if (ret)
+ goto cleanup;
+
+ ret = k5_unmarshal_cred(req.reply.ptr, req.reply.len, 4, &cred);
+ if (ret)
+ goto cleanup;
+
+ /* In rare cases we might retrieve a credential with a session key this
+ * context can't support, in which case we must retry using iteration. */
+ if (flags & KRB5_TC_SUPPORTED_KTYPES) {
+ ret = krb5_get_tgs_ktypes(context, cred.server, &enctypes);
+ if (ret)
+ goto cleanup;
+ if (!k5_etypes_contains(enctypes, cred.keyblock.enctype)) {
+ ret = k5_cc_retrieve_cred_default(context, cache, flags, mcred,
+ cred_out);
+ goto cleanup;
+ }
+ }
+
+ *cred_out = cred;
+ memset(&cred, 0, sizeof(cred));
+
+cleanup:
+ kcmreq_free(&req);
+ krb5_free_cred_contents(context, &cred);
+ free(enctypes);
+ /* Heimdal's KCM returns KRB5_CC_END if no cred is found. */
+ return (ret == KRB5_CC_END) ? KRB5_CC_NOTFOUND : map_invalid(ret);
}
static krb5_error_code KRB5_CALLCONV
diff --git a/src/tests/kcmserver.py b/src/tests/kcmserver.py
index 8c5e66ff1..25e6f2bbe 100644
--- a/src/tests/kcmserver.py
+++ b/src/tests/kcmserver.py
@@ -40,6 +40,7 @@ class KCMOpcodes(object):
INITIALIZE = 4
DESTROY = 5
STORE = 6
+ RETRIEVE = 7
GET_PRINCIPAL = 8
GET_CRED_UUID_LIST = 9
GET_CRED_BY_UUID = 10
@@ -54,6 +55,7 @@ class KCMOpcodes(object):
class KRB5Errors(object):
+ KRB5_CC_NOTFOUND = -1765328243
KRB5_CC_END = -1765328242
KRB5_CC_NOSUPP = -1765328137
KRB5_FCC_NOFILE = -1765328189
@@ -86,11 +88,29 @@ def get_cache(name):
return cache
+def unpack_data(argbytes):
+ dlen, = struct.unpack('>L', argbytes[:4])
+ return argbytes[4:dlen+4], argbytes[dlen+4:]
+
+
def unmarshal_name(argbytes):
offset = argbytes.find(b'\0')
return argbytes[0:offset], argbytes[offset+1:]
+def unmarshal_princ(argbytes):
+ # Ignore the type at argbytes[0:4].
+ ncomps, = struct.unpack('>L', argbytes[4:8])
+ realm, rest = unpack_data(argbytes[8:])
+ comps = []
+ for i in range(ncomps):
+ comp, rest = unpack_data(rest)
+ comps.append(comp)
+ # Asssume no quoting is needed.
+ princ = b'/'.join(comps) + b'@' + realm
+ return princ, rest
+
+
def op_gen_new(argbytes):
# Does not actually check for uniqueness.
global next_unique
@@ -126,6 +146,22 @@ def op_store(argbytes):
return 0, b''
+def op_retrieve(argbytes):
+ name, rest = unmarshal_name(argbytes)
+ # Ignore the flags at rest[0:4] and the header at rest[4:8].
+ # Assume there are client and server creds in the tag and match
+ # only against them.
+ cprinc, rest = unmarshal_princ(rest[8:])
+ sprinc, rest = unmarshal_princ(rest)
+ cache = get_cache(name)
+ for cred in (cache.creds[u] for u in cache.cred_uuids):
+ cred_cprinc, rest = unmarshal_princ(cred)
+ cred_sprinc, rest = unmarshal_princ(rest)
+ if cred_cprinc == cprinc and cred_sprinc == sprinc:
+ return 0, cred
+ return KRB5Errors.KRB5_CC_NOTFOUND, b''
+
+
def op_get_principal(argbytes):
name, rest = unmarshal_name(argbytes)
cache = get_cache(name)
@@ -199,6 +235,7 @@ ophandlers = {
KCMOpcodes.INITIALIZE : op_initialize,
KCMOpcodes.DESTROY : op_destroy,
KCMOpcodes.STORE : op_store,
+ KCMOpcodes.RETRIEVE : op_retrieve,
KCMOpcodes.GET_PRINCIPAL : op_get_principal,
KCMOpcodes.GET_CRED_UUID_LIST : op_get_cred_uuid_list,
KCMOpcodes.GET_CRED_BY_UUID : op_get_cred_by_uuid,
@@ -243,10 +280,11 @@ def service_request(s):
return True
parser = optparse.OptionParser()
-parser.add_option('-c', '--credlist', action='store_true', dest='credlist',
- default=False, help='Support KCM_OP_GET_CRED_LIST')
+parser.add_option('-f', '--fallback', action='store_true', dest='fallback',
+ default=False, help='Do not support RETRIEVE/GET_CRED_LIST')
(options, args) = parser.parse_args()
-if not options.credlist:
+if options.fallback:
+ del ophandlers[KCMOpcodes.RETRIEVE]
del ophandlers[KCMOpcodes.GET_CRED_LIST]
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
diff --git a/src/tests/t_ccache.py b/src/tests/t_ccache.py
index 90040fb7b..6ea9fb969 100755
--- a/src/tests/t_ccache.py
+++ b/src/tests/t_ccache.py
@@ -25,7 +25,7 @@ from k5test import *
kcm_socket_path = os.path.join(os.getcwd(), 'testdir', 'kcm')
conf = {'libdefaults': {'kcm_socket': kcm_socket_path,
'kcm_mach_service': '-'}}
-realm = K5Realm(create_host=False, krb5_conf=conf)
+realm = K5Realm(krb5_conf=conf)
keyctl = which('keyctl')
out = realm.run([klist, '-c', 'KEYRING:process:abcd'], expected_code=1)
@@ -71,6 +71,11 @@ def collection_test(realm, ccname):
realm.kinit('alice', password('alice'))
realm.run([klist], expected_msg='Default principal: alice@')
realm.run([klist, '-A', '-s'])
+ realm.run([kvno, realm.host_princ], expected_msg = 'kvno = 1')
+ realm.run([kvno, realm.host_princ], expected_msg = 'kvno = 1')
+ out = realm.run([klist])
+ if out.count(realm.host_princ) != 1:
+ fail('Wrong number of service tickets in cache')
realm.run([kdestroy])
output = realm.run([klist], expected_code=1)
if 'No credentials cache' not in output and 'not found' not in output:
@@ -126,14 +131,14 @@ def collection_test(realm, ccname):
collection_test(realm, 'DIR:' + os.path.join(realm.testdir, 'cc'))
-# Test KCM without and with GET_CRED_LIST support.
+# Test KCM with and without RETRIEVE and GET_CRED_LIST support.
kcmserver_path = os.path.join(srctop, 'tests', 'kcmserver.py')
kcmd = realm.start_server([sys.executable, kcmserver_path, kcm_socket_path],
'starting...')
collection_test(realm, 'KCM:')
stop_daemon(kcmd)
os.remove(kcm_socket_path)
-realm.start_server([sys.executable, kcmserver_path, '-c', kcm_socket_path],
+realm.start_server([sys.executable, kcmserver_path, '-f', kcm_socket_path],
'starting...')
collection_test(realm, 'KCM:')

View File

@ -1,53 +0,0 @@
From a9bc03fe03ef4b00bcdad13c99bb4c376a8b9964 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 10 Jul 2018 16:17:15 -0400
Subject: [PATCH] Use SHA-256 instead of MD5 for audit ticket IDs
ticket: 8711 (new)
(cherry picked from commit c1e1bfa26bd2f045e88e6013c500fca9428c98f3)
---
src/kdc/kdc_audit.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/src/kdc/kdc_audit.c b/src/kdc/kdc_audit.c
index c9a7f9f9d..f40913dc8 100644
--- a/src/kdc/kdc_audit.c
+++ b/src/kdc/kdc_audit.c
@@ -146,7 +146,7 @@ kau_make_tkt_id(krb5_context context,
{
krb5_error_code ret = 0;
char *hash = NULL, *ptr;
- krb5_checksum cksum;
+ uint8_t hashbytes[K5_SHA256_HASHLEN];
unsigned int i;
*out = NULL;
@@ -154,19 +154,18 @@ kau_make_tkt_id(krb5_context context,
if (ticket == NULL)
return EINVAL;
- ret = krb5_c_make_checksum(context, CKSUMTYPE_RSA_MD5, NULL, 0,
- &ticket->enc_part.ciphertext, &cksum);
+ ret = k5_sha256(&ticket->enc_part.ciphertext, 1, hashbytes);
if (ret)
return ret;
- hash = k5alloc(cksum.length * 2 + 1, &ret);
- if (hash != NULL) {
- for (i = 0, ptr = hash; i < cksum.length; i++, ptr += 2)
- snprintf(ptr, 3, "%02X", cksum.contents[i]);
- *ptr = '\0';
- *out = hash;
- }
- krb5_free_checksum_contents(context, &cksum);
+ hash = k5alloc(sizeof(hashbytes) * 2 + 1, &ret);
+ if (hash == NULL)
+ return ret;
+
+ for (i = 0, ptr = hash; i < sizeof(hashbytes); i++, ptr += 2)
+ snprintf(ptr, 3, "%02X", hashbytes[i]);
+ *ptr = '\0';
+ *out = hash;
return 0;
}

View File

@ -0,0 +1,124 @@
From baa2a485190d1b31f3dae06a18dc24d71dbe35bf Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Fri, 11 Mar 2022 12:04:14 +0100
Subject: [PATCH] Use SHA-256 instead of SHA-1 for PKINIT CMS digest
Various organizations including NIST have been strongly recommending to
stop using SHA-1 for digital signatures for some years already. CMS
digest is used to generate such signatures, hence it should be upgraded
to use SHA-256.
---
.../preauth/pkinit/pkinit_crypto_openssl.c | 40 ++++++++++---------
1 file changed, 22 insertions(+), 18 deletions(-)
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index dbb054378..32291e3ac 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -1234,7 +1234,7 @@ cms_signeddata_create(krb5_context context,
/* will not fill-out EVP_PKEY because it's on the smartcard */
/* Set digest algs */
- p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha1);
+ p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha256);
if (p7si->digest_alg->parameter != NULL)
ASN1_TYPE_free(p7si->digest_alg->parameter);
@@ -1245,17 +1245,18 @@ cms_signeddata_create(krb5_context context,
/* Set sig algs */
if (p7si->digest_enc_alg->parameter != NULL)
ASN1_TYPE_free(p7si->digest_enc_alg->parameter);
- p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_sha1WithRSAEncryption);
+ p7si->digest_enc_alg->algorithm =
+ OBJ_nid2obj(NID_sha256WithRSAEncryption);
if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new()))
goto cleanup;
p7si->digest_enc_alg->parameter->type = V_ASN1_NULL;
/* add signed attributes */
- /* compute sha1 digest over the EncapsulatedContentInfo */
+ /* compute sha256 digest over the EncapsulatedContentInfo */
ctx = EVP_MD_CTX_new();
if (ctx == NULL)
goto cleanup;
- EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
+ EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
EVP_DigestUpdate(ctx, data, data_len);
md_tmp = EVP_MD_CTX_md(ctx);
EVP_DigestFinal_ex(ctx, md_data, &md_len);
@@ -1283,12 +1284,14 @@ cms_signeddata_create(krb5_context context,
goto cleanup2;
#ifndef WITHOUT_PKCS11
- /* Some tokens can only do RSAEncryption without sha1 hash */
- /* to compute sha1WithRSAEncryption, encode the algorithm ID for the hash
- * function and the hash value into an ASN.1 value of type DigestInfo
- * DigestInfo::=SEQUENCE {
- * digestAlgorithm AlgorithmIdentifier,
- * digest OCTET STRING }
+ /*
+ * Some tokens can only do RSAEncryption without a hash. To compute
+ * sha256WithRSAEncryption, encode the algorithm ID for the hash
+ * function and the hash value into an ASN.1 value of type DigestInfo:
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm AlgorithmIdentifier,
+ * digest OCTET STRING
+ * }
*/
if (id_cryptoctx->pkcs11_method == 1 &&
id_cryptoctx->mech == CKM_RSA_PKCS) {
@@ -1304,7 +1307,7 @@ cms_signeddata_create(krb5_context context,
alg = X509_ALGOR_new();
if (alg == NULL)
goto cleanup2;
- X509_ALGOR_set0(alg, OBJ_nid2obj(NID_sha1), V_ASN1_NULL, NULL);
+ X509_ALGOR_set0(alg, OBJ_nid2obj(NID_sha256), V_ASN1_NULL, NULL);
alg_len = i2d_X509_ALGOR(alg, NULL);
digest = ASN1_OCTET_STRING_new();
@@ -1333,7 +1336,7 @@ cms_signeddata_create(krb5_context context,
#endif
{
pkiDebug("mech = %s\n",
- id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA1_RSA_PKCS" : "FS");
+ id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA256_RSA_PKCS" : "FS");
retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen,
&sig, &sig_len);
}
@@ -4147,7 +4150,7 @@ create_signature(unsigned char **sig, unsigned int *sig_len,
ctx = EVP_MD_CTX_new();
if (ctx == NULL)
return ENOMEM;
- EVP_SignInit(ctx, EVP_sha1());
+ EVP_SignInit(ctx, EVP_sha256());
EVP_SignUpdate(ctx, data, data_len);
*sig_len = EVP_PKEY_size(pkey);
if ((*sig = malloc(*sig_len)) == NULL)
@@ -4623,10 +4626,11 @@ pkinit_get_certs_pkcs11(krb5_context context,
#ifndef PKINIT_USE_MECH_LIST
/*
- * We'd like to use CKM_SHA1_RSA_PKCS for signing if it's available, but
- * many cards seems to be confused about whether they are capable of
- * this or not. The safe thing seems to be to ignore the mechanism list,
- * always use CKM_RSA_PKCS and calculate the sha1 digest ourselves.
+ * We'd like to use CKM_SHA256_RSA_PKCS for signing if it's available, but
+ * historically many cards seem to be confused about whether they are
+ * capable of mechanisms or not. The safe thing seems to be to ignore the
+ * mechanism list, always use CKM_RSA_PKCS and calculate the sha256 digest
+ * ourselves.
*/
id_cryptoctx->mech = CKM_RSA_PKCS;
@@ -4654,7 +4658,7 @@ pkinit_get_certs_pkcs11(krb5_context context,
if (mechp[i] == CKM_RSA_PKCS) {
/* This seems backwards... */
id_cryptoctx->mech =
- (info.flags & CKF_SIGN) ? CKM_SHA1_RSA_PKCS : CKM_RSA_PKCS;
+ (info.flags & CKF_SIGN) ? CKM_SHA256_RSA_PKCS : CKM_RSA_PKCS;
}
}
free(mechp);
--
2.35.1

View File

@ -1,62 +0,0 @@
From c5df16a88027d7f9b6eb53b1c3fa949d6538616b Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 26 Mar 2018 11:24:49 -0400
Subject: [PATCH] Use k5_buf_init_dynamic_zap where appropriate
(cherry picked from commit 9172599008f3a6790d4a9a67acff58049742dcb6)
---
src/lib/krb5/ccache/cc_file.c | 4 ++--
src/lib/krb5/ccache/cc_keyring.c | 2 +-
src/util/support/utf8_conv.c | 4 +++-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/lib/krb5/ccache/cc_file.c b/src/lib/krb5/ccache/cc_file.c
index 6789c09e1..9263a0054 100644
--- a/src/lib/krb5/ccache/cc_file.c
+++ b/src/lib/krb5/ccache/cc_file.c
@@ -758,7 +758,7 @@ fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
memset(creds, 0, sizeof(*creds));
k5_cc_mutex_lock(context, &data->lock);
- k5_buf_init_dynamic(&buf);
+ k5_buf_init_dynamic_zap(&buf);
ret = krb5_lock_file(context, fileno(fcursor->fp), KRB5_LOCKMODE_SHARED);
if (ret)
@@ -982,7 +982,7 @@ fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
goto cleanup;
/* Marshal the cred and write it to the file with a single append write. */
- k5_buf_init_dynamic(&buf);
+ k5_buf_init_dynamic_zap(&buf);
k5_marshal_cred(&buf, version, creds);
ret = k5_buf_status(&buf);
if (ret)
diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c
index fba710b1b..8419f6ebf 100644
--- a/src/lib/krb5/ccache/cc_keyring.c
+++ b/src/lib/krb5/ccache/cc_keyring.c
@@ -1295,7 +1295,7 @@ krcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
goto errout;
/* Serialize credential using the file ccache version 4 format. */
- k5_buf_init_dynamic(&buf);
+ k5_buf_init_dynamic_zap(&buf);
k5_marshal_cred(&buf, 4, creds);
ret = k5_buf_status(&buf);
if (ret)
diff --git a/src/util/support/utf8_conv.c b/src/util/support/utf8_conv.c
index 5cfc2c512..08cef4168 100644
--- a/src/util/support/utf8_conv.c
+++ b/src/util/support/utf8_conv.c
@@ -99,7 +99,9 @@ k5_utf8_to_utf16le(const char *utf8, uint8_t **utf16_out, size_t *nbytes_out)
*utf16_out = NULL;
*nbytes_out = 0;
- k5_buf_init_dynamic(&buf);
+ /* UTF-16 conversion is used for RC4 string-to-key, so treat this data as
+ * sensitive. */
+ k5_buf_init_dynamic_zap(&buf);
/* Examine next UTF-8 character. */
while (*utf8 != '\0') {

View File

@ -1,869 +0,0 @@
From 19109505ad04efdfd70df3ee922e22bcf5a294f3 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 19 Feb 2018 00:52:35 -0500
Subject: [PATCH] Use libkrb5support hex functions where appropriate
(cherry picked from commit b0c700608be7455041a8afc0e4502e8783ee7f30)
---
src/kadmin/dbutil/deps | 16 ++---
src/kadmin/dbutil/tabdump.c | 19 +++---
src/kadmin/ktutil/deps | 13 ++--
src/kadmin/ktutil/ktutil_funcs.c | 30 ++++-----
src/lib/crypto/crypto_tests/deps | 39 ++++++-----
src/lib/crypto/crypto_tests/t_cksum.c | 35 +++-------
src/lib/crypto/crypto_tests/t_crc.c | 28 ++------
src/lib/crypto/crypto_tests/t_hmac.c | 34 +++++-----
src/plugins/kdb/ldap/ldap_util/deps | 18 ++---
.../kdb/ldap/ldap_util/kdb5_ldap_services.c | 32 +++------
.../kdb/ldap/ldap_util/kdb5_ldap_services.h | 2 -
src/plugins/kdb/ldap/libkdb_ldap/deps | 19 +++---
.../kdb/ldap/libkdb_ldap/ldap_service_stash.c | 65 +++----------------
.../kdb/ldap/libkdb_ldap/ldap_service_stash.h | 3 -
.../kdb/ldap/libkdb_ldap/libkdb_ldap.exports | 1 -
src/slave/deps | 15 +++--
src/slave/kproplog.c | 11 ++--
src/tests/gssapi/deps | 14 ++--
src/tests/gssapi/t_prf.c | 13 ++--
19 files changed, 152 insertions(+), 255 deletions(-)
diff --git a/src/kadmin/dbutil/deps b/src/kadmin/dbutil/deps
index 4dcc33628..8b0965aac 100644
--- a/src/kadmin/dbutil/deps
+++ b/src/kadmin/dbutil/deps
@@ -185,14 +185,14 @@ $(OUTPRE)tabdump.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
$(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
$(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
- $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
- $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h kdb5_util.h tabdump.c \
- tdumputil.h
+ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/kdb_log.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ kdb5_util.h tabdump.c tdumputil.h
$(OUTPRE)tdumputil.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
diff --git a/src/kadmin/dbutil/tabdump.c b/src/kadmin/dbutil/tabdump.c
index fb36b060a..2f313dbb0 100644
--- a/src/kadmin/dbutil/tabdump.c
+++ b/src/kadmin/dbutil/tabdump.c
@@ -32,6 +32,7 @@
#include <k5-int.h>
#include "k5-platform.h" /* for asprintf */
+#include "k5-hex.h"
#include <limits.h>
#include <stdio.h>
@@ -230,9 +231,7 @@ static int
write_data(struct rec_args *args, krb5_data *data)
{
int ret;
- char *p;
- size_t i;
- struct k5buf buf;
+ char *hex;
struct rechandle *h = args->rh;
struct tdopts *opts = args->opts;
@@ -241,17 +240,15 @@ write_data(struct rec_args *args, krb5_data *data)
return -1;
return 0;
}
- k5_buf_init_dynamic(&buf);
- p = data->data;
- for (i = 0; i < data->length; i++)
- k5_buf_add_fmt(&buf, "%02x", (unsigned char)p[i]);
- if (buf.data == NULL) {
- errno = ENOMEM;
+ ret = k5_hex_encode(data->data, data->length, FALSE, &hex);
+ if (ret) {
+ errno = ret;
return -1;
}
- ret = writefield(h, "%s", (char *)buf.data);
- k5_buf_free(&buf);
+
+ ret = writefield(h, "%s", hex);
+ free(hex);
return ret;
}
diff --git a/src/kadmin/ktutil/deps b/src/kadmin/ktutil/deps
index 4df399924..5863e63c7 100644
--- a/src/kadmin/ktutil/deps
+++ b/src/kadmin/ktutil/deps
@@ -18,9 +18,10 @@ $(OUTPRE)ktutil_funcs.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
$(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
- $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
- $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h ktutil.h ktutil_funcs.c
+ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ ktutil.h ktutil_funcs.c
diff --git a/src/kadmin/ktutil/ktutil_funcs.c b/src/kadmin/ktutil/ktutil_funcs.c
index 7a3aa0dca..5843e24b7 100644
--- a/src/kadmin/ktutil/ktutil_funcs.c
+++ b/src/kadmin/ktutil/ktutil_funcs.c
@@ -29,6 +29,7 @@
*/
#include "k5-int.h"
+#include "k5-hex.h"
#include "ktutil.h"
#include <string.h>
#include <ctype.h>
@@ -106,9 +107,8 @@ krb5_error_code ktutil_add(context, list, princ_str, kvno,
krb5_keyblock key;
char buf[BUFSIZ];
char promptstr[1024];
-
- char *cp;
- int i, tmp;
+ uint8_t *keybytes;
+ size_t keylen;
unsigned int pwsize = BUFSIZ;
retval = krb5_parse_name(context, princ_str, &princ);
@@ -199,24 +199,18 @@ krb5_error_code ktutil_add(context, list, princ_str, kvno,
goto cleanup;
}
- lp->entry->key.enctype = enctype;
- lp->entry->key.contents = (krb5_octet *) malloc((strlen(buf) + 1) / 2);
- if (!lp->entry->key.contents) {
- retval = ENOMEM;
+ retval = k5_hex_decode(buf, &keybytes, &keylen);
+ if (retval) {
+ if (retval == EINVAL) {
+ fprintf(stderr, _("addent: Illegal character in key.\n"));
+ retval = 0;
+ }
goto cleanup;
}
- i = 0;
- for (cp = buf; *cp; cp += 2) {
- if (!isxdigit((int) cp[0]) || !isxdigit((int) cp[1])) {
- fprintf(stderr, _("addent: Illegal character in key.\n"));
- retval = 0;
- goto cleanup;
- }
- sscanf(cp, "%02x", &tmp);
- lp->entry->key.contents[i++] = (krb5_octet) tmp;
- }
- lp->entry->key.length = i;
+ lp->entry->key.enctype = enctype;
+ lp->entry->key.contents = keybytes;
+ lp->entry->key.length = keylen;
}
lp->entry->principal = princ;
lp->entry->vno = kvno;
diff --git a/src/lib/crypto/crypto_tests/deps b/src/lib/crypto/crypto_tests/deps
index bc5422a06..5d94a593d 100644
--- a/src/lib/crypto/crypto_tests/deps
+++ b/src/lib/crypto/crypto_tests/deps
@@ -73,12 +73,13 @@ $(OUTPRE)t_hmac.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(srcdir)/../builtin/crypto_mod.h $(srcdir)/../builtin/sha2/sha2.h \
$(srcdir)/../krb/crypto_int.h $(top_srcdir)/include/k5-buf.h \
$(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
- $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
- $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h t_hmac.c
+ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ t_hmac.c
$(OUTPRE)t_pkcs5.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
@@ -143,12 +144,13 @@ $(OUTPRE)t_cksum.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
$(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
- $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
- $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h t_cksum.c
+ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ t_cksum.c
$(OUTPRE)t_cksums.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
@@ -165,12 +167,13 @@ $(OUTPRE)t_crc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(srcdir)/../builtin/crypto_mod.h $(srcdir)/../builtin/sha2/sha2.h \
$(srcdir)/../krb/crypto_int.h $(top_srcdir)/include/k5-buf.h \
$(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
- $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
- $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h t_crc.c
+ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ t_crc.c
$(OUTPRE)t_mddriver.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../builtin/aes/aes.h \
diff --git a/src/lib/crypto/crypto_tests/t_cksum.c b/src/lib/crypto/crypto_tests/t_cksum.c
index 2200fe76e..0edaeb850 100644
--- a/src/lib/crypto/crypto_tests/t_cksum.c
+++ b/src/lib/crypto/crypto_tests/t_cksum.c
@@ -27,6 +27,7 @@
/* Test checksum and checksum compatability for rsa-md[4,5]-des. */
#include "k5-int.h"
+#include "k5-hex.h"
#define MD5_K5BETA_COMPAT
#define MD4_K5BETA_COMPAT
@@ -50,29 +51,6 @@ print_checksum(char *text, int number, char *message, krb5_checksum *checksum)
printf("\n");
}
-static void
-parse_hexstring(const char *s, krb5_checksum *cksum)
-{
- size_t i, len;
- unsigned int byte;
- unsigned char *cp;
-
- len = strlen(s);
- cp = malloc(len / 2);
- cksum->contents = cp;
- if (cp == NULL) {
- cksum->length = 0;
- return;
- }
- cksum->length = len / 2;
- for (i = 0; i + 1 < len; i += 2) {
- sscanf(&s[i], "%2x", &byte);
- *cp++ = byte;
- }
- cksum->checksum_type = CKTYPE;
- cksum->magic = KV5M_CHECKSUM;
-}
-
/*
* Test the checksum verification of Old Style (tm) and correct RSA-MD[4,5]-DES
* checksums.
@@ -86,6 +64,7 @@ main(argc, argv)
char **argv;
{
int msgindex;
+ size_t len;
krb5_boolean valid;
krb5_keyblock keyblock;
krb5_key key;
@@ -150,12 +129,14 @@ main(argc, argv)
free(checksum.contents);
/* Verify a known-good checksum for this plaintext. */
- parse_hexstring(argv[msgindex+1], &knowncksum);
- if (knowncksum.contents == NULL) {
- printf("parse_hexstring failed\n");
- kret = 1;
+ kret = k5_hex_decode(argv[msgindex + 1], &knowncksum.contents, &len);
+ if (kret) {
+ printf("k5_hex_decode failed\n");
break;
}
+ knowncksum.length = len;
+ knowncksum.checksum_type = CKTYPE;
+ knowncksum.magic = KV5M_CHECKSUM;
kret = krb5_k_verify_checksum(NULL, key, 0, &plaintext, &knowncksum,
&valid);
if (kret != 0) {
diff --git a/src/lib/crypto/crypto_tests/t_crc.c b/src/lib/crypto/crypto_tests/t_crc.c
index 190773252..1a35cfba5 100644
--- a/src/lib/crypto/crypto_tests/t_crc.c
+++ b/src/lib/crypto/crypto_tests/t_crc.c
@@ -32,6 +32,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <k5-hex.h>
#include "crypto_int.h"
#define HEX 1
@@ -139,31 +140,12 @@ timetest(unsigned int nblk, unsigned int blksiz)
}
#endif
-static void gethexstr(char *data, size_t *outlen, unsigned char *outbuf,
- size_t buflen)
-{
- size_t inlen;
- char *cp, buf[3];
- long n;
-
- inlen = strlen(data);
- *outlen = 0;
- for (cp = data; (size_t) (cp - data) < inlen; cp += 2) {
- strncpy(buf, cp, 2);
- buf[2] = '\0';
- n = strtol(buf, NULL, 16);
- outbuf[(*outlen)++] = n;
- if (*outlen > buflen)
- break;
- }
-}
-
static void
verify(void)
{
unsigned int i;
struct crc_trial trial;
- unsigned char buf[4];
+ uint8_t *bytes;
size_t len;
unsigned long cksum;
char *typestr;
@@ -179,9 +161,11 @@ verify(void)
break;
case HEX:
typestr = "HEX";
- gethexstr(trial.data, &len, buf, 4);
+ if (k5_hex_decode(trial.data, &bytes, &len) != 0)
+ abort();
cksum = 0;
- mit_crc32(buf, len, &cksum);
+ mit_crc32(bytes, len, &cksum);
+ free(bytes);
break;
default:
typestr = "BOGUS";
diff --git a/src/lib/crypto/crypto_tests/t_hmac.c b/src/lib/crypto/crypto_tests/t_hmac.c
index 8961380ea..93d54828f 100644
--- a/src/lib/crypto/crypto_tests/t_hmac.c
+++ b/src/lib/crypto/crypto_tests/t_hmac.c
@@ -34,6 +34,7 @@
#include <string.h>
#include <ctype.h>
+#include <k5-hex.h>
#include "crypto_int.h"
#define ASIZE(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))
@@ -136,12 +137,10 @@ static void test_hmac()
{
krb5_keyblock key;
krb5_data in, out;
- char outbuf[20];
- char stroutbuf[80];
+ char outbuf[20], *hexdigest;
krb5_error_code err;
- unsigned int i, j;
+ unsigned int i;
int lose = 0;
- struct k5buf buf;
/* RFC 2202 test vector. */
static const struct hmac_test md5tests[] = {
@@ -151,13 +150,13 @@ static void test_hmac()
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
},
8, "Hi There",
- "0x9294727a3638bb1c13f48ef8158bfc9d"
+ "9294727a3638bb1c13f48ef8158bfc9d"
},
{
4, "Jefe",
28, "what do ya want for nothing?",
- "0x750c783e6ab0b503eaa86e310a5db738"
+ "750c783e6ab0b503eaa86e310a5db738"
},
{
@@ -172,7 +171,7 @@ static void test_hmac()
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
},
- "0x56be34521d144c88dbb8c733f0e8b3f6"
+ "56be34521d144c88dbb8c733f0e8b3f6"
},
{
@@ -188,7 +187,7 @@ static void test_hmac()
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
},
- "0x697eaf0aca3a3aea3a75164746ffaa79"
+ "697eaf0aca3a3aea3a75164746ffaa79"
},
{
@@ -197,7 +196,7 @@ static void test_hmac()
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c
},
20, "Test With Truncation",
- "0x56461ef2342edc00f9bab995690efd4c"
+ "56461ef2342edc00f9bab995690efd4c"
},
{
@@ -212,7 +211,7 @@ static void test_hmac()
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
},
54, "Test Using Larger Than Block-Size Key - Hash Key First",
- "0x6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"
+ "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"
},
{
@@ -228,7 +227,7 @@ static void test_hmac()
},
73,
"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
- "0x6f630fad67cda0ee1fb1f562db3aa53e"
+ "6f630fad67cda0ee1fb1f562db3aa53e"
},
};
@@ -246,19 +245,16 @@ static void test_hmac()
exit(1);
}
- k5_buf_init_fixed(&buf, stroutbuf, sizeof(stroutbuf));
- k5_buf_add(&buf, "0x");
- for (j = 0; j < out.length; j++)
- k5_buf_add_fmt(&buf, "%02x", 0xff & outbuf[j]);
- if (k5_buf_status(&buf) != 0)
+ if (k5_hex_encode(out.data, out.length, FALSE, &hexdigest) != 0)
abort();
- if (strcmp(stroutbuf, md5tests[i].hexdigest)) {
+ if (strcmp(hexdigest, md5tests[i].hexdigest)) {
printf("*** CHECK FAILED!\n"
- "\tReturned: %s.\n"
- "\tExpected: %s.\n", stroutbuf, md5tests[i].hexdigest);
+ "\tReturned: 0x%s.\n"
+ "\tExpected: 0x%s.\n", hexdigest, md5tests[i].hexdigest);
lose++;
} else
printf("Matches expected result.\n");
+ free(hexdigest);
}
/* Do again with SHA-1 tests.... */
diff --git a/src/plugins/kdb/ldap/ldap_util/deps b/src/plugins/kdb/ldap/ldap_util/deps
index 75d4dd0cf..be0194c00 100644
--- a/src/plugins/kdb/ldap/ldap_util/deps
+++ b/src/plugins/kdb/ldap/ldap_util/deps
@@ -89,15 +89,15 @@ $(OUTPRE)kdb5_ldap_services.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(srcdir)/../libkdb_ldap/ldap_krbcontainer.h $(srcdir)/../libkdb_ldap/ldap_misc.h \
$(srcdir)/../libkdb_ldap/ldap_realm.h $(top_srcdir)/include/k5-buf.h \
$(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
- $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
- $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
- $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
- $(top_srcdir)/lib/kdb/kdb5.h kdb5_ldap_list.h kdb5_ldap_policy.h \
- kdb5_ldap_realm.h kdb5_ldap_services.c kdb5_ldap_services.h \
- kdb5_ldap_util.h
+ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h $(top_srcdir)/lib/kdb/kdb5.h \
+ kdb5_ldap_list.h kdb5_ldap_policy.h kdb5_ldap_realm.h \
+ kdb5_ldap_services.c kdb5_ldap_services.h kdb5_ldap_util.h
$(OUTPRE)getdate.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
getdate.c
diff --git a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.c b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.c
index 3d6994c67..ce038fc3d 100644
--- a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.c
+++ b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.c
@@ -37,6 +37,7 @@
*/
#include <k5-int.h>
+#include <k5-hex.h>
#include "kdb5_ldap_util.h"
#include "kdb5_ldap_list.h"
@@ -96,11 +97,10 @@ kdb5_ldap_stash_service_password(int argc, char **argv)
char *service_object = NULL;
char *file_name = NULL, *tmp_file = NULL;
char passwd[MAX_SERVICE_PASSWD_LEN];
- char *str = NULL;
+ char *str = NULL, *hexpasswd = NULL;
char line[MAX_LEN];
FILE *pfile = NULL;
krb5_boolean print_usage = FALSE;
- krb5_data hexpasswd = {0, 0, NULL};
mode_t old_mode = 0;
/*
@@ -183,21 +183,12 @@ kdb5_ldap_stash_service_password(int argc, char **argv)
}
/* Convert the password to hexadecimal */
- {
- krb5_data pwd;
-
- pwd.length = passwd_len;
- pwd.data = passwd;
-
- ret = tohex(pwd, &hexpasswd);
- if (ret != 0) {
- com_err(me, ret,
- _("Failed to convert the password to hexadecimal"));
- memset(passwd, 0, passwd_len);
- goto cleanup;
- }
+ ret = k5_hex_encode(passwd, passwd_len, FALSE, &hexpasswd);
+ zap(passwd, passwd_len);
+ if (ret != 0) {
+ com_err(me, ret, _("Failed to convert the password to hexadecimal"));
+ goto cleanup;
}
- memset(passwd, 0, passwd_len);
/* TODO: file lock for the service password file */
@@ -225,7 +216,7 @@ kdb5_ldap_stash_service_password(int argc, char **argv)
if (str == NULL) {
if (feof(pfile)) {
/* If the service object dn is not present in the service password file */
- if (fprintf(pfile, "%s#{HEX}%s\n", service_object, hexpasswd.data) < 0) {
+ if (fprintf(pfile, "%s#{HEX}%s\n", service_object, hexpasswd) < 0) {
com_err(me, errno,
_("Failed to write service object password to file"));
fclose(pfile);
@@ -277,7 +268,7 @@ kdb5_ldap_stash_service_password(int argc, char **argv)
while (fgets(line, MAX_LEN, pfile) != NULL) {
if (((str = strstr(line, service_object)) != NULL) &&
(line[strlen(service_object)] == '#')) {
- if (fprintf(newfile, "%s#{HEX}%s\n", service_object, hexpasswd.data) < 0) {
+ if (fprintf(newfile, "%s#{HEX}%s\n", service_object, hexpasswd) < 0) {
com_err(me, errno, _("Failed to write service object "
"password to file"));
fclose(newfile);
@@ -322,10 +313,7 @@ kdb5_ldap_stash_service_password(int argc, char **argv)
cleanup:
- if (hexpasswd.length != 0) {
- memset(hexpasswd.data, 0, hexpasswd.length);
- free(hexpasswd.data);
- }
+ zapfreestr(hexpasswd);
if (service_object)
free(service_object);
diff --git a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.h b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.h
index cf652c578..08af62e17 100644
--- a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.h
+++ b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_services.h
@@ -32,6 +32,4 @@
#define MAX_LEN 1024
#define MAX_SERVICE_PASSWD_LEN 256
-extern int tohex(krb5_data, krb5_data *);
-
extern void kdb5_ldap_stash_service_password(int argc, char **argv);
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/deps b/src/plugins/kdb/ldap/libkdb_ldap/deps
index 1ff28553f..afca604dc 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/deps
+++ b/src/plugins/kdb/ldap/libkdb_ldap/deps
@@ -220,15 +220,16 @@ ldap_service_stash.so ldap_service_stash.po $(OUTPRE)ldap_service_stash.$(OBJEXT
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
$(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
- $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
- $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
- $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
- $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
- $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h $(top_srcdir)/lib/kdb/kdb5.h \
- kdb_ldap.h ldap_handle.h ldap_krbcontainer.h ldap_main.h \
- ldap_misc.h ldap_realm.h ldap_service_stash.c ldap_service_stash.h
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-hex.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ $(top_srcdir)/lib/kdb/kdb5.h kdb_ldap.h ldap_handle.h \
+ ldap_krbcontainer.h ldap_main.h ldap_misc.h ldap_realm.h \
+ ldap_service_stash.c ldap_service_stash.h
kdb_xdr.so kdb_xdr.po $(OUTPRE)kdb_xdr.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_stash.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_stash.c
index 87a2118ff..cb30f4a7f 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_stash.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_stash.c
@@ -31,16 +31,16 @@
#include "ldap_main.h"
#include "kdb_ldap.h"
#include "ldap_service_stash.h"
+#include <k5-hex.h>
#include <ctype.h>
/* Decode a password of the form {HEX}<hexstring>. */
static krb5_error_code
dec_password(krb5_context context, const char *str, char **password_out)
{
+ krb5_error_code ret;
+ uint8_t *bytes;
size_t len;
- const unsigned char *p;
- unsigned char *password, *q;
- unsigned int k;
*password_out = NULL;
@@ -48,30 +48,15 @@ dec_password(krb5_context context, const char *str, char **password_out)
k5_setmsg(context, EINVAL, _("Not a hexadecimal password"));
return EINVAL;
}
- str += 5;
- len = strlen(str);
- if (len % 2 != 0) {
- k5_setmsg(context, EINVAL, _("Password corrupt"));
- return EINVAL;
+ ret = k5_hex_decode(str + 5, &bytes, &len);
+ if (ret) {
+ if (ret == EINVAL)
+ k5_setmsg(context, ret, _("Password corrupt"));
+ return ret;
}
- q = password = malloc(len / 2 + 1);
- if (password == NULL)
- return ENOMEM;
-
- for (p = (unsigned char *)str; *p != '\0'; p += 2) {
- if (!isxdigit(*p) || !isxdigit(p[1])) {
- free(password);
- k5_setmsg(context, EINVAL, _("Password corrupt"));
- return EINVAL;
- }
- sscanf((char *)p, "%2x", &k);
- *q++ = k;
- }
- *q = '\0';
-
- *password_out = (char *)password;
+ *password_out = (char *)bytes;
return 0;
}
@@ -128,35 +113,3 @@ krb5_ldap_readpassword(krb5_context context, const char *filename,
/* Extract the plain password information. */
return dec_password(context, val, password_out);
}
-
-/* Encodes a sequence of bytes in hexadecimal */
-
-int
-tohex(krb5_data in, krb5_data *ret)
-{
- unsigned int i=0;
- int err = 0;
-
- ret->length = 0;
- ret->data = NULL;
-
- ret->data = malloc((unsigned int)in.length * 2 + 1 /*Null termination */);
- if (ret->data == NULL) {
- err = ENOMEM;
- goto cleanup;
- }
- ret->length = in.length * 2;
- ret->data[ret->length] = 0;
-
- for (i = 0; i < in.length; i++)
- snprintf(ret->data + 2 * i, 3, "%02x", in.data[i] & 0xff);
-
-cleanup:
-
- if (ret->length == 0) {
- free(ret->data);
- ret->data = NULL;
- }
-
- return err;
-}
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_stash.h b/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_stash.h
index dbf62443a..03cf9a1f7 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_stash.h
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_service_stash.h
@@ -37,7 +37,4 @@ krb5_error_code
krb5_ldap_readpassword(krb5_context context, const char *filename,
const char *name, char **password_out);
-int
-tohex(krb5_data, krb5_data *);
-
#endif
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports b/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports
index 2342f1db8..5376d3453 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports
+++ b/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports
@@ -1,4 +1,3 @@
-tohex
krb5_ldap_open
krb5_ldap_close
krb5_ldap_db_init
diff --git a/src/slave/deps b/src/slave/deps
index c3677a5e1..c0f558ecd 100644
--- a/src/slave/deps
+++ b/src/slave/deps
@@ -64,10 +64,11 @@ $(OUTPRE)kproplog.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
$(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
$(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
- $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
- $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h kproplog.c
+ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/kdb_log.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ kproplog.c
diff --git a/src/slave/kproplog.c b/src/slave/kproplog.c
index 4f19eeb8c..d4aed7ba6 100644
--- a/src/slave/kproplog.c
+++ b/src/slave/kproplog.c
@@ -9,6 +9,7 @@
*/
#include "k5-int.h"
+#include "k5-hex.h"
#include <locale.h>
#include <sys/types.h>
#include <sys/mman.h>
@@ -106,15 +107,15 @@ print_deltat(uint32_t *deltat)
static void
print_hex(const char *tag, utf8str_t *str)
{
- unsigned int i;
unsigned int len;
+ char *hex;
len = str->utf8str_t_len;
- printf("\t\t\t%s(%d): 0x", tag, len);
- for (i = 0; i < len; i++)
- printf("%02x", (krb5_octet)str->utf8str_t_val[i]);
- printf("\n");
+ if (k5_hex_encode(str->utf8str_t_val, len, FALSE, &hex) != 0)
+ abort();
+ printf("\t\t\t%s(%d): 0x%s\n", tag, len, hex);
+ free(hex);
}
/* Display string primitive. */
diff --git a/src/tests/gssapi/deps b/src/tests/gssapi/deps
index b784deb63..0b50d9ed3 100644
--- a/src/tests/gssapi/deps
+++ b/src/tests/gssapi/deps
@@ -149,13 +149,13 @@ $(OUTPRE)t_prf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(srcdir)/../../lib/gssapi/krb5/gssapiP_krb5.h $(srcdir)/../../lib/gssapi/krb5/gssapi_krb5.h \
$(srcdir)/../../lib/gssapi/mechglue/mechglue.h $(srcdir)/../../lib/gssapi/mechglue/mglueP.h \
$(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
- $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
- $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
- $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
- $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
- $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
- $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
- common.h t_prf.c
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-hex.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h common.h t_prf.c
$(OUTPRE)t_s4u.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
$(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
$(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
diff --git a/src/tests/gssapi/t_prf.c b/src/tests/gssapi/t_prf.c
index 2c8c85188..6a698ce0f 100644
--- a/src/tests/gssapi/t_prf.c
+++ b/src/tests/gssapi/t_prf.c
@@ -24,6 +24,7 @@
*/
#include "k5-int.h"
+#include "k5-hex.h"
#include "common.h"
#include "mglueP.h"
#include "gssapiP_krb5.h"
@@ -109,12 +110,14 @@ static struct {
static size_t
fromhex(const char *hexstr, unsigned char *out)
{
- const char *p;
- size_t count;
+ uint8_t *bytes;
+ size_t len;
- for (p = hexstr, count = 0; *p != '\0'; p += 2, count++)
- sscanf(p, "%2hhx", &out[count]);
- return count;
+ if (k5_hex_decode(hexstr, &bytes, &len) != 0)
+ abort();
+ memcpy(out, bytes, len);
+ free(bytes);
+ return len;
}
int

View File

@ -0,0 +1,138 @@
From 2e871df888d526a15e9e91807480c15ca4e40618 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 26 Oct 2023 16:26:42 -0400
Subject: [PATCH] Wait indefinitely on KDC TCP connections
When making a KDC or password change request, wait indefinitely
(limited only by request_timeout if set) once a KDC has accepted a TCP
connection.
ticket: 9105 (new)
(cherry picked from commit 6436a3808061da787a43c6810f5f0370cdfb6e36)
---
doc/admin/conf_files/krb5_conf.rst | 2 +-
src/lib/krb5/os/sendto_kdc.c | 50 ++++++++++++++++--------------
2 files changed, 27 insertions(+), 25 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 557094f6a2..98fe231813 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -358,7 +358,7 @@ The libdefaults section may contain any of the following relations:
for initial ticket requests. The default value is 0.
**request_timeout**
- (:ref:`duration` string.) Sets the maximum total time for KDC or
+ (:ref:`duration` string.) Sets the maximum total time for KDC and
password change requests. This timeout does not affect the
intervals between requests, so setting a low timeout may result in
fewer requests being attempted and/or some servers not being
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index f57117126e..19b78aba24 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -134,7 +134,6 @@ struct conn_state {
krb5_data callback_buffer;
size_t server_index;
struct conn_state *next;
- time_ms endtime;
krb5_boolean defer;
struct {
const char *uri_path;
@@ -344,15 +343,19 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
struct select_state *out, int *sret)
{
#ifndef USE_POLL
- struct timeval tv;
+ struct timeval tv, *tvp;
#endif
krb5_error_code retval;
time_ms curtime, interval;
- retval = get_curtime_ms(&curtime);
- if (retval != 0)
- return retval;
- interval = (curtime < endtime) ? endtime - curtime : 0;
+ if (endtime != 0) {
+ retval = get_curtime_ms(&curtime);
+ if (retval != 0)
+ return retval;
+ interval = (curtime < endtime) ? endtime - curtime : 0;
+ } else {
+ interval = -1;
+ }
/* We don't need a separate copy of the selstate for poll, but use one for
* consistency with how we use select. */
@@ -361,9 +364,14 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
#ifdef USE_POLL
*sret = poll(out->fds, out->nfds, interval);
#else
- tv.tv_sec = interval / 1000;
- tv.tv_usec = interval % 1000 * 1000;
- *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, &tv);
+ if (interval != -1) {
+ tv.tv_sec = interval / 1000;
+ tv.tv_usec = interval % 1000 * 1000;
+ tvp = &tv;
+ } else {
+ tvp = NULL;
+ }
+ *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, tvp);
#endif
return (*sret < 0) ? SOCKET_ERRNO : 0;
@@ -1094,11 +1102,6 @@ service_tcp_connect(krb5_context context, const krb5_data *realm,
}
conn->state = WRITING;
-
- /* Record this connection's timeout for service_fds. */
- if (get_curtime_ms(&conn->endtime) == 0)
- conn->endtime += 10000;
-
return conn->service_write(context, realm, conn, selstate);
}
@@ -1373,19 +1376,18 @@ kill_conn:
return FALSE;
}
-/* Return the maximum of endtime and the endtime fields of all currently active
- * TCP connections. */
-static time_ms
-get_endtime(time_ms endtime, struct conn_state *conns)
+/* Return true if conns contains any states with connected TCP sockets. */
+static krb5_boolean
+any_tcp_connections(struct conn_state *conns)
{
struct conn_state *state;
for (state = conns; state != NULL; state = state->next) {
- if ((state->state == READING || state->state == WRITING) &&
- state->endtime > endtime)
- endtime = state->endtime;
+ if (state->addr.transport != UDP &&
+ (state->state == READING || state->state == WRITING))
+ return TRUE;
}
- return endtime;
+ return FALSE;
}
static krb5_boolean
@@ -1408,9 +1410,9 @@ service_fds(krb5_context context, struct select_state *selstate,
e = 0;
while (selstate->nfds > 0) {
- endtime = get_endtime(interval_end, conns);
+ endtime = any_tcp_connections(conns) ? 0 : interval_end;
/* Don't wait longer than the whole request should last. */
- if (timeout && endtime > timeout)
+ if (timeout && (!endtime || endtime > timeout))
endtime = timeout;
e = cm_select_or_poll(selstate, endtime, seltemp, &selret);
if (e == EINTR)
--
2.44.0

View File

@ -1,30 +0,0 @@
From 55a8161c3f5238df522447499a38bf2e9497b074 Mon Sep 17 00:00:00 2001
From: Dylan Gray <35609490+Dylan-MSFT@users.noreply.github.com>
Date: Fri, 13 Jul 2018 15:09:01 -0700
Subject: [PATCH] Zap copy of secret in RC4 string-to-key
Commit b8814745049b5f401e3ae39a81dc1e14598ae48c (ticket 8576) added a
zero-terminated copy of the input string in
krb5int_arcfour_string_to_key(). This copy should be zeroed when
freed as the input string typically contains a password.
[ghudson@mit.edu: rewrote commit message]
ticket: 8713 (new)
---
src/lib/crypto/krb/s2k_rc4.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib/crypto/krb/s2k_rc4.c b/src/lib/crypto/krb/s2k_rc4.c
index 081a91217..f7e699d60 100644
--- a/src/lib/crypto/krb/s2k_rc4.c
+++ b/src/lib/crypto/krb/s2k_rc4.c
@@ -25,7 +25,7 @@ krb5int_arcfour_string_to_key(const struct krb5_keytypes *ktp,
if (utf8 == NULL)
return err;
err = k5_utf8_to_utf16le(utf8, &copystr, &copystrlen);
- free(utf8);
+ zapfree(utf8, string->length);
if (err)
return err;

View File

@ -1,29 +0,0 @@
From 5d970e16e768a134e65ee7cf367b8f34a80e0980 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 27 Mar 2018 15:42:28 -0400
Subject: [PATCH] Zap data when freeing krb5_spake_factor
krb5_spake_factor structures will sometimes hold sensitive data when
second-factor SPAKE is implemented, so should be zapped when freed.
ticket: 8647
(cherry picked from commit 9cc94a3f1ce06a4430f684300a747ec079102403)
---
src/lib/krb5/krb/kfree.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c
index e1ea1494a..71e7fcad0 100644
--- a/src/lib/krb5/krb/kfree.c
+++ b/src/lib/krb5/krb/kfree.c
@@ -897,7 +897,9 @@ k5_free_spake_factor(krb5_context context, krb5_spake_factor *val)
{
if (val == NULL)
return;
- krb5_free_data(context, val->data);
+ if (val->data != NULL)
+ zapfree(val->data->data, val->data->length);
+ free(val->data);
free(val);
}

View File

@ -1,13 +1,16 @@
From 162ba7fbce23d82719956de1b126e48fe676e9d1 Mon Sep 17 00:00:00 2001
From 4e42a6786a06b7223f27536267492a463a700c76 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Tue, 23 Aug 2016 16:45:26 -0400
Subject: [PATCH] krb5-1.15-beta1-buildconf.patch
Subject: [PATCH] [downstream] Adjust build configuration
Build binaries in this package as RELRO PIEs, libraries as partial RELRO,
and install shared libraries with the execute bit set on them. Prune out
the -L/usr/lib* and PIE flags where they might leak out and affect
apps which just want to link with the libraries. FIXME: needs to check and
not just assume that the compiler supports using these flags.
Last-updated: krb5-1.15-beta1
(cherry picked from commit 92508996ed4c69fa6f5cf855fdf10f34cfa07ec9)
---
src/build-tools/krb5-config.in | 7 +++++++
src/config/pre.in | 2 +-
@ -33,7 +36,7 @@ index c17cb5eb5..1891dea99 100755
lib_flags="$lib_flags -lkdb5 $KDB5_DB_LIB"
library=krb5
diff --git a/src/config/pre.in b/src/config/pre.in
index d4714d29a..03f5c8890 100644
index 917357df9..a8540ae2a 100644
--- a/src/config/pre.in
+++ b/src/config/pre.in
@@ -185,7 +185,7 @@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ $(INSTALL_STRIP)

View File

@ -0,0 +1,126 @@
From 274464a6faaee694c30ae4d1412a8ab516b1a982 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Wed, 20 Sep 2023 16:22:06 +0200
Subject: [PATCH] [downstream] Allow to make AD-SIGNEDPATH optional
MIT krb5 1.20 and newer KDCs do generate a minimal PAC instead of
AD-SIGNEDPATH. As a consequence, an evidence ticket generated by an
older KDC would fail to be processed by a newer KDC for a constrained
delegation request.
This commit modifies this behavior to check the AD-SIGNEDPATH whenever
it is present in a TGS-REQ, but do not require it in case a PAC is
provided AND the KDB plugin supports its verification. This is done
regardless to the fact the constrained delegation request is from a
local realm or a cross-realm.
To enable this mechanism, the KDB plugin must set the
"optional_ab_signedpath" string attribute to "true" for the local TGS
principal.
---
src/include/kdb.h | 1 +
src/kdc/kdc_authdata.c | 65 +++++++++++++++++++++++++++++++++---------
2 files changed, 52 insertions(+), 14 deletions(-)
diff --git a/src/include/kdb.h b/src/include/kdb.h
index c56947ab81..95d07d0195 100644
--- a/src/include/kdb.h
+++ b/src/include/kdb.h
@@ -136,6 +136,7 @@
/* String attribute names recognized by krb5 */
#define KRB5_KDB_SK_SESSION_ENCTYPES "session_enctypes"
#define KRB5_KDB_SK_REQUIRE_AUTH "require_auth"
+#define KRB5_KDB_SK_OPTIONAL_AD_SIGNEDPATH "optional_ad_signedpath"
#if !defined(_WIN32)
diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c
index 1ebe872467..c0fcccdf21 100644
--- a/src/kdc/kdc_authdata.c
+++ b/src/kdc/kdc_authdata.c
@@ -668,6 +668,13 @@ has_pac(krb5_context context, krb5_authdata **authdata)
return has_kdc_issued_authdata(context, authdata, KRB5_AUTHDATA_WIN2K_PAC);
}
+/* Return true if the AD-SIGNEDPATH is present in authorization data. */
+static krb5_boolean
+has_ad_signedpath(krb5_context context, krb5_authdata **authdata)
+{
+ return has_kdc_issued_authdata(context, authdata, KRB5_AUTHDATA_SIGNTICKET);
+}
+
/* Verify AD-SIGNTICKET authdata if we need to, and insert an AD-SIGNEDPATH
* element if we should. */
static krb5_error_code
@@ -680,24 +687,54 @@ handle_signticket(krb5_context context, unsigned int flags,
{
krb5_error_code ret = 0;
krb5_principal *deleg_path = NULL;
- krb5_boolean signed_path = FALSE;
- krb5_boolean s4u2proxy;
+ krb5_boolean s4u2proxy, adsp_present, adsp_optional, adsp_valid = FALSE;
+ char *str;
s4u2proxy = isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
- /* For cross-realm the Windows PAC must have been verified, and it
- * fulfills the same role as the signed path. */
- if (req->msg_type == KRB5_TGS_REQ &&
- (!isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM) ||
- !has_pac(context, enc_tkt_req->authorization_data))) {
- ret = verify_signedpath(context, local_tgt, local_tgt_key, enc_tkt_req,
- &deleg_path, &signed_path);
- if (ret)
- goto cleanup;
+ if (req->msg_type == KRB5_TGS_REQ) {
+ adsp_present = has_ad_signedpath(context,
+ enc_tkt_req->authorization_data);
+
+ /* In case of constained delegation, based on the value of the
+ * "optional_ad_signedpath" string attribute of the local TGS principal:
+ * - "true": in case AD-SIGNEDPATH is absent, the PAC must be present
+ * - "false" or undefined: AD-SIGNEDPATH must be present
+ */
+ if (s4u2proxy && !adsp_present) {
+ ret = krb5_dbe_get_string(context, local_tgt,
+ KRB5_KDB_SK_OPTIONAL_AD_SIGNEDPATH,
+ &str);
+ /* TODO: should be using _krb5_conf_boolean(), but os-proto.h is not
+ * available here.
+ */
+ adsp_optional = !ret && str && (strncasecmp(str, "true", 4) == 0
+ || strncasecmp(str, "t", 1) == 0
+ || strncasecmp(str, "yes", 3) == 0
+ || strncasecmp(str, "y", 1) == 0
+ || strncasecmp(str, "1", 1) == 0
+ || strncasecmp(str, "on", 2) == 0);
+
+ if (!adsp_optional ||
+ !has_pac(context, enc_tkt_req->authorization_data)) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto cleanup;
+ }
+ }
- if (s4u2proxy && signed_path == FALSE) {
- ret = KRB5KDC_ERR_BADOPTION;
- goto cleanup;
+ /* If AD-SIGNEDPATH is present, verify it */
+ if (adsp_present) {
+ ret = verify_signedpath(context, local_tgt, local_tgt_key,
+ enc_tkt_req, &deleg_path, &adsp_valid);
+ if (ret)
+ goto cleanup;
+
+ /* In case of contrained delegation, if AD-SIGNEDPATH is present, it
+ * has to be valid */
+ if (s4u2proxy && !adsp_valid) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto cleanup;
+ }
}
}
--
2.41.0

View File

@ -1,33 +1,212 @@
From e04d483890496a1747f3cdb20a4df5285147f59b Mon Sep 17 00:00:00 2001
From f87e8a6734726bdd166f33757232a8c7cf9a9058 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Tue, 31 Jul 2018 13:47:26 -0400
Subject: [PATCH] In FIPS mode, add plaintext fallback for RC4 usages and taint
Date: Fri, 9 Nov 2018 15:12:21 -0500
Subject: [PATCH] [downstream] FIPS with PRNG and RADIUS and MD4+5
(cherry picked from commit a327e3bf5b992ac829c7b2d3317fb7d93b1c88ef)
(cherry picked from commit 2bd85da058d2d73eb2818a8e64656fec9b21b3c3)
NB: Use openssl's PRNG in FIPS mode and taint within krad.
A lot of the FIPS error conditions from OpenSSL are incredibly
mysterious (at best, things return NULL unexpectedly; at worst,
internal assertions are tripped; most of the time, you just get
ENOMEM). In order to cope with this, we need to have some level of
awareness of what we can and can't safely call.
This will slow down some calls slightly (FIPS_mode() takes multiple
locks), but not for any ciphers we care about - which is to say that
AES is fine. Shame about SPAKE though.
post6 restores MD4 (and therefore keygen-only RC4).
post7 restores MD5 and adds radius_md5_fips_override.
Last-updated: krb5-1.17
(cherry picked from commit bf8521bfaa4a4d54f6eb94f785c68942f4afa055)
---
src/lib/krad/attr.c | 45 +++++++++++++++++++++++++++++-----------
src/lib/krad/attrset.c | 5 +++--
src/lib/krad/internal.h | 13 ++++++++++--
src/lib/krad/packet.c | 22 +++++++++++---------
src/lib/krad/remote.c | 10 +++++++--
src/lib/krad/t_attr.c | 3 ++-
src/lib/krad/t_attrset.c | 4 +++-
7 files changed, 72 insertions(+), 30 deletions(-)
doc/admin/conf_files/krb5_conf.rst | 6 +++
src/lib/crypto/krb/prng.c | 11 ++++-
.../crypto/openssl/enc_provider/camellia.c | 6 +++
src/lib/crypto/openssl/enc_provider/rc4.c | 13 +++++-
.../crypto/openssl/hash_provider/hash_evp.c | 12 +++++
src/lib/crypto/openssl/hmac.c | 6 ++-
src/lib/krad/attr.c | 46 ++++++++++++++-----
src/lib/krad/attrset.c | 5 +-
src/lib/krad/internal.h | 28 ++++++++++-
src/lib/krad/packet.c | 22 +++++----
src/lib/krad/remote.c | 10 +++-
src/lib/krad/t_attr.c | 3 +-
src/lib/krad/t_attrset.c | 4 +-
src/plugins/preauth/spake/spake_client.c | 6 +++
src/plugins/preauth/spake/spake_kdc.c | 6 +++
15 files changed, 151 insertions(+), 33 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 1d2aa7f68..3a8b9cf47 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -331,6 +331,12 @@ The libdefaults section may contain any of the following relations:
qualification of shortnames, set this relation to the empty string
with ``qualify_shortname = ""``. (New in release 1.18.)
+**radius_md5_fips_override**
+ Downstream-only option to enable use of MD5 in RADIUS
+ communication (libkrad). This allows for local (or protected
+ tunnel) communication with a RADIUS server that doesn't use krad
+ (e.g., freeradius) while in FIPS mode.
+
**rdns**
If this flag is true, reverse name lookup will be used in addition
to forward name lookup to canonicalizing hostnames for use in
diff --git a/src/lib/crypto/krb/prng.c b/src/lib/crypto/krb/prng.c
index cb9ca9b98..f0e9984ca 100644
--- a/src/lib/crypto/krb/prng.c
+++ b/src/lib/crypto/krb/prng.c
@@ -26,6 +26,8 @@
#include "crypto_int.h"
+#include <openssl/rand.h>
+
krb5_error_code KRB5_CALLCONV
krb5_c_random_seed(krb5_context context, krb5_data *data)
{
@@ -99,9 +101,16 @@ krb5_boolean
k5_get_os_entropy(unsigned char *buf, size_t len, int strong)
{
const char *device;
-#if defined(__linux__) && defined(SYS_getrandom)
int r;
+ /* A wild FIPS mode appeared! */
+ if (FIPS_mode()) {
+ /* The return codes on this API are not good */
+ r = RAND_bytes(buf, len);
+ return r == 1;
+ }
+
+#if defined(__linux__) && defined(SYS_getrandom)
while (len > 0) {
/*
* Pull from the /dev/urandom pool, but require it to have been seeded.
diff --git a/src/lib/crypto/openssl/enc_provider/camellia.c b/src/lib/crypto/openssl/enc_provider/camellia.c
index 2da691329..f79679a0b 100644
--- a/src/lib/crypto/openssl/enc_provider/camellia.c
+++ b/src/lib/crypto/openssl/enc_provider/camellia.c
@@ -304,6 +304,9 @@ krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
unsigned char blockY[CAMELLIA_BLOCK_SIZE], blockB[CAMELLIA_BLOCK_SIZE];
struct iov_cursor cursor;
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
if (output->length < CAMELLIA_BLOCK_SIZE)
return KRB5_BAD_MSIZE;
@@ -331,6 +334,9 @@ static krb5_error_code
krb5int_camellia_init_state (const krb5_keyblock *key, krb5_keyusage usage,
krb5_data *state)
{
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
state->length = 16;
state->data = (void *) malloc(16);
if (state->data == NULL)
diff --git a/src/lib/crypto/openssl/enc_provider/rc4.c b/src/lib/crypto/openssl/enc_provider/rc4.c
index a65d57b7a..6ccaca94a 100644
--- a/src/lib/crypto/openssl/enc_provider/rc4.c
+++ b/src/lib/crypto/openssl/enc_provider/rc4.c
@@ -66,6 +66,9 @@ k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data,
EVP_CIPHER_CTX *ctx = NULL;
struct arcfour_state *arcstate;
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
arcstate = (state != NULL) ? (void *)state->data : NULL;
if (arcstate != NULL) {
ctx = arcstate->ctx;
@@ -113,7 +116,12 @@ k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data,
static void
k5_arcfour_free_state(krb5_data *state)
{
- struct arcfour_state *arcstate = (void *)state->data;
+ struct arcfour_state *arcstate;
+
+ if (FIPS_mode())
+ return;
+
+ arcstate = (void *) state->data;
EVP_CIPHER_CTX_free(arcstate->ctx);
free(arcstate);
@@ -125,6 +133,9 @@ k5_arcfour_init_state(const krb5_keyblock *key,
{
struct arcfour_state *arcstate;
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
/*
* The cipher state here is a saved pointer to a struct arcfour_state
* object, rather than a flat byte array as in most enc providers. The
diff --git a/src/lib/crypto/openssl/hash_provider/hash_evp.c b/src/lib/crypto/openssl/hash_provider/hash_evp.c
index 1e0fb8fc3..2eb5139c0 100644
--- a/src/lib/crypto/openssl/hash_provider/hash_evp.c
+++ b/src/lib/crypto/openssl/hash_provider/hash_evp.c
@@ -49,6 +49,11 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
if (ctx == NULL)
return ENOMEM;
+ if (type == EVP_md4() || type == EVP_md5()) {
+ /* See comments below in hash_md4() and hash_md5(). */
+ EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+ }
+
ok = EVP_DigestInit_ex(ctx, type, NULL);
for (i = 0; i < num_data; i++) {
if (!SIGN_IOV(&data[i]))
@@ -64,12 +69,19 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
static krb5_error_code
hash_md4(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
{
+ /*
+ * MD4 is needed in FIPS mode to perform key generation for RC4 keys used
+ * by IPA. These keys are only used along a (separately) secured channel
+ * for legacy reasons when performing trusts to Active Directory.
+ */
return hash_evp(EVP_md4(), data, num_data, output);
}
static krb5_error_code
hash_md5(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
{
+ /* MD5 is needed in FIPS mode for communication with RADIUS servers. This
+ * is gated in libkrad by libdefaults->radius_md5_fips_override. */
return hash_evp(EVP_md5(), data, num_data, output);
}
diff --git a/src/lib/crypto/openssl/hmac.c b/src/lib/crypto/openssl/hmac.c
index 7dc59dcc0..769a50c00 100644
--- a/src/lib/crypto/openssl/hmac.c
+++ b/src/lib/crypto/openssl/hmac.c
@@ -103,7 +103,11 @@ map_digest(const struct krb5_hash_provider *hash)
return EVP_sha256();
else if (!strncmp(hash->hash_name, "SHA-384",7))
return EVP_sha384();
- else if (!strncmp(hash->hash_name, "MD5", 3))
+
+ if (FIPS_mode())
+ return NULL;
+
+ if (!strncmp(hash->hash_name, "MD5", 3))
return EVP_md5();
else if (!strncmp(hash->hash_name, "MD4", 3))
return EVP_md4();
diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
index 9c13d9d75..275327e67 100644
index 9c13d9d75..42d354a3b 100644
--- a/src/lib/krad/attr.c
+++ b/src/lib/krad/attr.c
@@ -30,6 +30,7 @@
#include <k5-int.h>
#include "internal.h"
+#include <openssl/crypto.h>
#include <string.h>
/* RFC 2865 */
@@ -38,7 +39,8 @@
@@ -38,7 +38,8 @@
typedef krb5_error_code
(*attribute_transform_fn)(krb5_context ctx, const char *secret,
const unsigned char *auth, const krb5_data *in,
@ -37,7 +216,7 @@ index 9c13d9d75..275327e67 100644
typedef struct {
const char *name;
@@ -51,12 +53,14 @@ typedef struct {
@@ -51,12 +52,14 @@ typedef struct {
static krb5_error_code
user_password_encode(krb5_context ctx, const char *secret,
const unsigned char *auth, const krb5_data *in,
@ -54,7 +233,7 @@ index 9c13d9d75..275327e67 100644
static const attribute_record attributes[UCHAR_MAX] = {
{"User-Name", 1, MAX_ATTRSIZE, NULL, NULL},
@@ -128,7 +132,8 @@ static const attribute_record attributes[UCHAR_MAX] = {
@@ -128,7 +131,8 @@ static const attribute_record attributes[UCHAR_MAX] = {
static krb5_error_code
user_password_encode(krb5_context ctx, const char *secret,
const unsigned char *auth, const krb5_data *in,
@ -64,20 +243,21 @@ index 9c13d9d75..275327e67 100644
{
const unsigned char *indx;
krb5_error_code retval;
@@ -154,8 +159,14 @@ user_password_encode(krb5_context ctx, const char *secret,
@@ -154,8 +158,15 @@ user_password_encode(krb5_context ctx, const char *secret,
for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) {
memcpy(tmp.data + seclen, indx, BLOCKSIZE);
- retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
- &sum);
+ if (FIPS_mode()) {
+ if (kr_use_fips(ctx)) {
+ /* Skip encryption here. Taint so that we won't pass it out of
+ * the machine by accident. */
+ *is_fips = TRUE;
+ sum.contents = calloc(1, BLOCKSIZE);
+ } else
+ } else {
+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
+ &sum);
+ }
if (retval != 0) {
zap(tmp.data, tmp.length);
zap(outbuf, len);
@ -91,24 +271,25 @@ index 9c13d9d75..275327e67 100644
{
const unsigned char *indx;
krb5_error_code retval;
@@ -204,8 +216,14 @@ user_password_decode(krb5_context ctx, const char *secret,
@@ -204,8 +216,15 @@ user_password_decode(krb5_context ctx, const char *secret,
for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) {
memcpy(tmp.data + seclen, indx, BLOCKSIZE);
- retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
- &tmp, &sum);
+ if (FIPS_mode()) {
+ if (kr_use_fips(ctx)) {
+ /* Skip encryption here. Taint so that we won't pass it out of
+ * the machine by accident. */
+ *is_fips = TRUE;
+ sum.contents = calloc(1, BLOCKSIZE);
+ } else
+ } else {
+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
+ &tmp, &sum);
+ }
if (retval != 0) {
zap(tmp.data, tmp.length);
zap(outbuf, in->length);
@@ -248,7 +266,7 @@ krb5_error_code
@@ -248,7 +267,7 @@ krb5_error_code
kr_attr_encode(krb5_context ctx, const char *secret,
const unsigned char *auth, krad_attr type,
const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE],
@ -117,7 +298,7 @@ index 9c13d9d75..275327e67 100644
{
krb5_error_code retval;
@@ -265,7 +283,8 @@ kr_attr_encode(krb5_context ctx, const char *secret,
@@ -265,7 +284,8 @@ kr_attr_encode(krb5_context ctx, const char *secret,
return 0;
}
@ -127,7 +308,7 @@ index 9c13d9d75..275327e67 100644
}
krb5_error_code
@@ -274,6 +293,7 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
@@ -274,6 +294,7 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
{
krb5_error_code retval;
@ -135,7 +316,7 @@ index 9c13d9d75..275327e67 100644
retval = kr_attr_valid(type, in);
if (retval != 0)
@@ -288,7 +308,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
@@ -288,7 +309,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
return 0;
}
@ -169,10 +350,19 @@ index 03c613716..d89982a13 100644
return retval;
diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
index 996a89372..a53ce31ce 100644
index 996a89372..312dc8258 100644
--- a/src/lib/krad/internal.h
+++ b/src/lib/krad/internal.h
@@ -49,6 +49,13 @@
@@ -39,6 +39,8 @@
#include <sys/socket.h>
#include <netdb.h>
+#include <openssl/crypto.h>
+
#ifndef UCHAR_MAX
#define UCHAR_MAX 255
#endif
@@ -49,6 +51,13 @@
typedef struct krad_remote_st krad_remote;
@ -186,7 +376,7 @@ index 996a89372..a53ce31ce 100644
/* Validate constraints of an attribute. */
krb5_error_code
kr_attr_valid(krad_attr type, const krb5_data *data);
@@ -57,7 +64,8 @@ kr_attr_valid(krad_attr type, const krb5_data *data);
@@ -57,7 +66,8 @@ kr_attr_valid(krad_attr type, const krb5_data *data);
krb5_error_code
kr_attr_encode(krb5_context ctx, const char *secret, const unsigned char *auth,
krad_attr type, const krb5_data *in,
@ -196,7 +386,7 @@ index 996a89372..a53ce31ce 100644
/* Decode an attribute. */
krb5_error_code
@@ -69,7 +77,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
@@ -69,7 +79,8 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
krb5_error_code
kr_attrset_encode(const krad_attrset *set, const char *secret,
const unsigned char *auth,
@ -206,19 +396,29 @@ index 996a89372..a53ce31ce 100644
/* Decode attributes from a buffer. */
krb5_error_code
@@ -152,4 +163,17 @@ gai_error_code(int err)
}
}
+static inline krb5_boolean
+kr_use_fips(krb5_context ctx)
+{
+ int val = 0;
+
+ if (!FIPS_mode())
+ return 0;
+
+ profile_get_boolean(ctx->profile, "libdefaults",
+ "radius_md5_fips_override", NULL, 0, &val);
+ return !val;
+}
+
#endif /* INTERNAL_H_ */
diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
index c597174b6..794ac84c4 100644
index c597174b6..fc2d24800 100644
--- a/src/lib/krad/packet.c
+++ b/src/lib/krad/packet.c
@@ -32,6 +32,7 @@
#include <string.h>
#include <arpa/inet.h>
+#include <openssl/crypto.h>
typedef unsigned char uchar;
@@ -53,12 +54,6 @@ typedef unsigned char uchar;
@@ -53,12 +53,6 @@ typedef unsigned char uchar;
#define pkt_auth(p) ((uchar *)offset(&(p)->pkt, OFFSET_AUTH))
#define pkt_attr(p) ((unsigned char *)offset(&(p)->pkt, OFFSET_ATTR))
@ -231,19 +431,20 @@ index c597174b6..794ac84c4 100644
typedef struct {
uchar x[(UCHAR_MAX + 1) / 8];
} idmap;
@@ -187,8 +182,13 @@ auth_generate_response(krb5_context ctx, const char *secret,
@@ -187,8 +181,14 @@ auth_generate_response(krb5_context ctx, const char *secret,
memcpy(data.data + response->pkt.length, secret, strlen(secret));
/* Hash it. */
- retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
- &hash);
+ if (FIPS_mode()) {
+ if (kr_use_fips(ctx)) {
+ /* This checksum does very little security-wise anyway, so don't
+ * taint. */
+ hash.contents = calloc(1, AUTH_FIELD_SIZE);
+ } else
+ } else {
+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
+ &hash);
+ }
free(data.data);
if (retval != 0)
return retval;
@ -352,3 +553,51 @@ index 7928335ca..0f9576253 100644
krad_attrset_free(set);
/* Manually encode User-Name. */
diff --git a/src/plugins/preauth/spake/spake_client.c b/src/plugins/preauth/spake/spake_client.c
index 00734a13b..a3ce22b70 100644
--- a/src/plugins/preauth/spake/spake_client.c
+++ b/src/plugins/preauth/spake/spake_client.c
@@ -38,6 +38,8 @@
#include "groups.h"
#include <krb5/clpreauth_plugin.h>
+#include <openssl/crypto.h>
+
typedef struct reqstate_st {
krb5_pa_spake *msg; /* set in prep_questions, used in process */
krb5_keyblock *initial_key;
@@ -375,6 +377,10 @@ clpreauth_spake_initvt(krb5_context context, int maj_ver, int min_ver,
if (maj_ver != 1)
return KRB5_PLUGIN_VER_NOTSUPP;
+
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
vt = (krb5_clpreauth_vtable)vtable;
vt->name = "spake";
vt->pa_type_list = pa_types;
diff --git a/src/plugins/preauth/spake/spake_kdc.c b/src/plugins/preauth/spake/spake_kdc.c
index 88c964ce1..c7df0392f 100644
--- a/src/plugins/preauth/spake/spake_kdc.c
+++ b/src/plugins/preauth/spake/spake_kdc.c
@@ -41,6 +41,8 @@
#include <krb5/kdcpreauth_plugin.h>
+#include <openssl/crypto.h>
+
/*
* The SPAKE kdcpreauth module uses a secure cookie containing the following
* concatenated fields (all integer fields are big-endian):
@@ -571,6 +573,10 @@ kdcpreauth_spake_initvt(krb5_context context, int maj_ver, int min_ver,
if (maj_ver != 1)
return KRB5_PLUGIN_VER_NOTSUPP;
+
+ if (FIPS_mode())
+ return KRB5_CRYPTO_INTERNAL;
+
vt = (krb5_kdcpreauth_vtable)vtable;
vt->name = "spake";
vt->pa_type_list = pa_types;

Some files were not shown because too many files have changed in this diff Show More