366 lines
15 KiB
Diff
366 lines
15 KiB
Diff
|
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
|
||
|
|