diff --git a/SOURCES/Make-kprop-work-for-dump-files-larger-than-4GB.patch b/SOURCES/Make-kprop-work-for-dump-files-larger-than-4GB.patch new file mode 100644 index 0000000..6d1ffef --- /dev/null +++ b/SOURCES/Make-kprop-work-for-dump-files-larger-than-4GB.patch @@ -0,0 +1,365 @@ +From b778f10e31e566269768cba92a0005f35e134333 Mon Sep 17 00:00:00 2001 +From: Julien Rische +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 + #include + #include + #include +@@ -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 c2b2e8764..c0aa2c89b 100644 +--- a/src/kprop/kprop_util.c ++++ b/src/kprop/kprop_util.c +@@ -92,3 +92,45 @@ sn2princ_realm(krb5_context context, const char *hostname, const char *sname, + *princ_out = princ; + return 0; + } ++ ++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 c6b8efc28..dd6c697c0 100644 +--- a/src/kprop/kpropd.c ++++ b/src/kprop/kpropd.c +@@ -55,6 +55,7 @@ + #include "com_err.h" + #include "fake-addrinfo.h" + ++#include + #include + #include + #include +@@ -1358,9 +1359,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; + +@@ -1382,10 +1384,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); +@@ -1405,7 +1414,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); +@@ -1416,8 +1425,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); +@@ -1427,13 +1436,13 @@ recv_database(krb5_context context, int fd, int database_fd, + krb5_free_data_contents(context, &inbuf); + 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); + } +@@ -1444,7 +1453,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); + } +@@ -1454,9 +1464,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 received bytes"); +diff --git a/src/tests/t_kprop.py b/src/tests/t_kprop.py +index d96f7c560..4421a7c35 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.37.3 + diff --git a/SPECS/krb5.spec b/SPECS/krb5.spec index ee8f670..89d1541 100644 --- a/SPECS/krb5.spec +++ b/SPECS/krb5.spec @@ -42,7 +42,7 @@ Summary: The Kerberos network authentication system Name: krb5 Version: 1.19.1 -Release: %{?zdpd}22%{?dist} +Release: %{?zdpd}23%{?dist} # rharwood has trust path to signing key and verifies on check-in Source0: https://web.mit.edu/kerberos/dist/krb5/%{version}/krb5-%{version}%{?dashpre}.tar.gz @@ -98,6 +98,7 @@ Patch33: downstream-Allow-krad-UDP-TCP-localhost-connection-with-FIPS.patch Patch34: krb5-krad-remote.patch Patch35: krb5-krad-larger-attrs.patch Patch36: Set-reasonable-supportedCMSTypes-in-PKINIT.patch +Patch37: Make-kprop-work-for-dump-files-larger-than-4GB.patch License: MIT URL: https://web.mit.edu/kerberos/www/ @@ -655,6 +656,10 @@ exit 0 %{_libdir}/libkadm5srv_mit.so.* %changelog +* Tue Oct 18 2022 Julien Rische - 1.19.1-23 +- Fix kprop for propagating dump files larger than 4GB +- Resolves: rhbz#2133014 + * Fri Jul 08 2022 Julien Rische - 1.19.1-22 - Restore "supportedCMSTypes" attribute in PKINIT preauth requests - Set SHA-512 or SHA-256 with RSA as preferred CMS signature algorithms