From: Greg Hudson Date: Fri, 17 Jul 2015 17:03:35 +0000 (-0400) Subject: Fix compatibility with pre-1.11 iprop dump files X-Git-Tag: krb5-1.14-alpha1~63 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F298%2Fhead;p=thirdparty%2Fkrb5.git Fix compatibility with pre-1.11 iprop dump files Ticket #7223 added new policy fields and a new dump format version to marshal them, but did not add a new iprop dump format version. As a result, slave KDCs running 1.11 or later cannot receive full resyncs from master KDCs running 1.10 or earlier. (Reported by John Devitofranceschi.) Retroactively add support for pre-1.11 policy entries by making process_r1_11_policy() read the first ten fields, check whether the next whitespace character is a newline, and then read the rest if it is not. ticket: 8213 target_version: 1.13.3 --- diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c index bfb85772b4..07f62e909b 100644 --- a/src/kadmin/dbutil/dump.c +++ b/src/kadmin/dbutil/dump.c @@ -958,41 +958,61 @@ process_r1_11_policy(krb5_context context, const char *fname, FILE *filep, char namebuf[1024]; char keysaltbuf[KRB5_KDB_MAX_ALLOWED_KS_LEN + 1]; unsigned int refcnt; - int nread, ret = 0; + int nread, c, ret = 0; memset(&rec, 0, sizeof(rec)); (*linenop)++; rec.name = namebuf; - rec.allowed_keysalts = keysaltbuf; - nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t" - "%u\t%u\t%u\t" - K5CONST_WIDTH_SCANF_STR(KRB5_KDB_MAX_ALLOWED_KS_LEN) - "\t%hd", rec.name, &rec.pw_min_life, &rec.pw_max_life, + /* + * Due to a historical error, iprop dumps use the same version before and + * after the 1.11 policy extensions. So we need to accept both 1.8-format + * and 1.11-format policy entries. Begin by reading the 1.8 fields. + */ + nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u", + rec.name, &rec.pw_min_life, &rec.pw_max_life, &rec.pw_min_length, &rec.pw_min_classes, &rec.pw_history_num, &refcnt, &rec.pw_max_fail, - &rec.pw_failcnt_interval, &rec.pw_lockout_duration, - &rec.attributes, &rec.max_life, &rec.max_renewable_life, - rec.allowed_keysalts, &rec.n_tl_data); + &rec.pw_failcnt_interval, &rec.pw_lockout_duration); if (nread == EOF) return -1; - if (nread != 15) { + if (nread != 10) { fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); return 1; } - if (rec.allowed_keysalts && !strcmp(rec.allowed_keysalts, "-")) - rec.allowed_keysalts = NULL; + /* The next character should be a newline (1.8) or a tab (1.11). */ + c = getc(filep); + if (c == EOF) + return -1; + if (c != '\n') { + /* Read the additional 1.11-format fields. */ + rec.allowed_keysalts = keysaltbuf; + nread = fscanf(filep, "%u\t%u\t%u\t" + K5CONST_WIDTH_SCANF_STR(KRB5_KDB_MAX_ALLOWED_KS_LEN) + "\t%hd", &rec.attributes, &rec.max_life, + &rec.max_renewable_life, rec.allowed_keysalts, + &rec.n_tl_data); + if (nread == EOF) + return -1; + if (nread != 5) { + fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); + return 1; + } - /* Get TL data */ - ret = alloc_tl_data(rec.n_tl_data, &rec.tl_data); - if (ret) - goto cleanup; + if (rec.allowed_keysalts && !strcmp(rec.allowed_keysalts, "-")) + rec.allowed_keysalts = NULL; - ret = process_tl_data(fname, filep, *linenop, rec.tl_data); - if (ret) - goto cleanup; + /* Get TL data */ + ret = alloc_tl_data(rec.n_tl_data, &rec.tl_data); + if (ret) + goto cleanup; + + ret = process_tl_data(fname, filep, *linenop, rec.tl_data); + if (ret) + goto cleanup; + } ret = krb5_db_create_policy(context, &rec); if (ret) diff --git a/src/tests/t_dump.py b/src/tests/t_dump.py index 6ba0d35f70..5d3a437625 100755 --- a/src/tests/t_dump.py +++ b/src/tests/t_dump.py @@ -12,12 +12,15 @@ realm.run([kadminl, 'addpol', 'fred']) dumpfile = os.path.join(realm.testdir, 'dump') realm.run([kdb5_util, 'dump', dumpfile]) -# Write an additional policy record to the dump. +# Write additional policy records to the dump. Use the 1.8 format for +# one of them, to test retroactive compatibility (for issue #8213). f = open('testdir/dump', 'a') +f.write('policy compat 0 0 3 4 5 0 ' + '0 0 0\n') f.write('policy barney 0 0 1 1 1 0 ' '0 0 0 0 0 0 - 1 ' '2 28 ' - 'fd100f5064625f6372656174696f6e404b5242544553542e434f4d00') + 'fd100f5064625f6372656174696f6e404b5242544553542e434f4d00\n') f.close() # Destroy and load the database; check that the policies exist. @@ -33,6 +36,9 @@ if 'Expiration date: [never]' not in out or 'MKey: vno 1' not in out: out = realm.run([kadminl, 'getpols']) if 'fred\n' not in out or 'barney\n' not in out: fail('Missing policy after load') +out = realm.run([kadminl, 'getpol', 'compat']) +if 'Number of old keys kept: 5' not in out: + fail('Policy (1.8 format) has wrong value after load') out = realm.run([kadminl, 'getpol', 'barney']) if 'Number of old keys kept: 1' not in out: fail('Policy has wrong value after load') @@ -44,7 +50,7 @@ out = realm.run([kadminl, 'getprincs']) if realm.user_princ not in out or realm.host_princ not in out: fail('Missing principal after load') out = realm.run([kadminl, 'getpols']) -if 'fred\n' not in out or 'barney\n' not in out: +if 'compat\n' not in out or 'fred\n' not in out or 'barney\n' not in out: fail('Missing policy after second load') srcdumpdir = os.path.join(srctop, 'tests', 'dumpfiles')