From: Greg Hudson Date: Thu, 26 Apr 2018 03:50:06 +0000 (-0400) Subject: Add LMDB KDB module tests X-Git-Tag: krb5-1.17-beta1~118 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dafc1daeedb008faa9dc6246e33f37a5c45462ca;p=thirdparty%2Fkrb5.git Add LMDB KDB module tests Add a new k5test function multidb_realms() which yields a realm using BDB and a realm using LMDB. Run the tests in t_dump.py, t_iprop.py, the lockout tests in t_policy.py, and kdbtest (in t_kdb.py) with both databases. If K5TEST_LMDB is set in the environment, use LMDB for all Python tests and all tests in tests/dejagnu, with two exceptions (t_unlockiter.py and t_kdb_locking.py) which test BDB-specific behavior. To support those exceptions, add the "bdb_only" realm initialization flag. In t_iprop.py, be sure to properly clean up kpropd daemons, using a new k5test realm method stop_kpropd() to stop a kpropd daemon started with start_kpropd() and remove it from the realm's list. ticket: 8674 --- diff --git a/src/Makefile.in b/src/Makefile.in index e2c178f70d..bd7a2dc2a4 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -490,7 +490,7 @@ check-prerecurse: runenv.py $(RM) $(SKIPTESTS) touch $(SKIPTESTS) -check-unix: +check-unix: check-lmdb-$(HAVE_LMDB) cat $(SKIPTESTS) check-pytests-no: check-postrecurse @@ -501,6 +501,10 @@ check-cmocka-no: check-postrecurse @echo 'Skipped cmocka tests: cmocka library or header not found' >> \ $(SKIPTESTS) +check-lmdb-yes: +check-lmdb-no: + @echo 'Skipped LMDB tests: LMDB KDB module not built' >> $(SKIPTESTS) + # Create a test realm and spawn a shell in an environment pointing to it. # If CROSSNUM is set, create that many fully connected test realms and # point the shell at the first one. diff --git a/src/tests/dejagnu/config/default.exp b/src/tests/dejagnu/config/default.exp index 2d1686c56c..18b8c3347b 100644 --- a/src/tests/dejagnu/config/default.exp +++ b/src/tests/dejagnu/config/default.exp @@ -455,7 +455,8 @@ proc delete_db {} { global tmppwd # Master and slave db files file delete $tmppwd/kdc-db $tmppwd/kdc-db.ok $tmppwd/kdc-db.kadm5 \ - $tmppwd/kdc-db.kadm5.lock \ + $tmppwd/kdc-db.kadm5.lock $tmppwd/kdc-db.mdb $tmppwd/kdc-db.mdb-lock \ + $tmppwd/kdc-db.lockout.mdb $tmppwd/kdc-db.lockout.mdb-lock \ $tmppwd/kdc-db.ulog \ $tmppwd/slave-db $tmppwd/slave-db.ok $tmppwd/slave-db.kadm5 $tmppwd/slave-db.kadm5.lock \ $tmppwd/slave-db~ $tmppwd/slave-db~.ok $tmppwd/slave-db~.kadm5 $tmppwd/slave-db~.kadm5.lock @@ -933,6 +934,7 @@ proc setup_krb5_conf { {type client} } { global mode global portbase global srcdir + global env set pkinit_certs [findfile "[pwd]/$srcdir/pkinit-certs" "[pwd]/$srcdir/pkinit-certs" "$srcdir/pkinit-certs"] # Create a krb5.conf file. @@ -980,7 +982,7 @@ proc setup_krb5_conf { {type client} } { puts $conffile " kdc = $hostname:[expr 1 + $portbase]" puts $conffile " admin_server = $hostname:[expr 4 + $portbase]" puts $conffile " kpasswd_server = $hostname:[expr 5 + $portbase]" - puts $conffile " database_module = foo_db2" + puts $conffile " database_module = db" puts $conffile " \}" puts $conffile "" puts $conffile "\[domain_realm\]" @@ -993,8 +995,13 @@ proc setup_krb5_conf { {type client} } { puts $conffile "" puts $conffile "\[dbmodules\]" puts $conffile " db_module_dir = $tmppwd/../../../plugins/kdb" - puts $conffile " foo_db2 = {" - puts $conffile " db_library = db2" + puts $conffile " db = {" + if [info exists env(K5TEST_LMDB)] { + puts $conffile " db_library = klmdb" + puts $conffile " nosync = true" + } else { + puts $conffile " db_library = db2" + } puts $conffile " database_name = $tmppwd/$type-db" puts $conffile " }" close $conffile diff --git a/src/tests/dejagnu/krb-standalone/kprop.exp b/src/tests/dejagnu/krb-standalone/kprop.exp index cc1a26a287..f23ebe1a70 100644 --- a/src/tests/dejagnu/krb-standalone/kprop.exp +++ b/src/tests/dejagnu/krb-standalone/kprop.exp @@ -6,7 +6,7 @@ # processes when the procedure ends. proc setup_slave {} { - global tmppwd hostname REALMNAME + global tmppwd hostname REALMNAME env file delete $tmppwd/slave-stash $tmppwd/slave-acl file copy -force $tmppwd/stash:foo $tmppwd/slave-stash file copy -force $tmppwd/acl $tmppwd/slave-acl @@ -16,7 +16,12 @@ proc setup_slave {} { close $aclfile } file copy -force $tmppwd/adb.lock $tmppwd/slave-adb.lock - foreach suffix { {} .kadm5 .kadm5.lock .ok } { + if [info exists env(K5TEST_LMDB)] { + set suffixes { .mdb .mdb-lock .lockout.mdb .lockout.mdb-lock } + } else { + set suffixes { {} .kadm5 .kadm5.lock .ok } + } + foreach suffix $suffixes { file copy -force $tmppwd/kdc-db$suffix $tmppwd/slave-db$suffix } } diff --git a/src/tests/t_dump.py b/src/tests/t_dump.py index 2a424d9493..22cff00534 100755 --- a/src/tests/t_dump.py +++ b/src/tests/t_dump.py @@ -2,82 +2,12 @@ from k5test import * from filecmp import cmp -# Make sure we can dump and load an ordinary database, and that -# principals and policies survive a dump/load cycle. - -realm = K5Realm(start_kdc=False) -realm.run([kadminl, 'addpol', 'fred']) - -# Create a dump file. -dumpfile = os.path.join(realm.testdir, 'dump') -realm.run([kdb5_util, 'dump', dumpfile]) - -# 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\n') -f.close() - -# Destroy and load the database; check that the policies exist. -# Spot-check principal and policy fields. -mark('reload after dump') -realm.run([kdb5_util, 'destroy', '-f']) -realm.run([kdb5_util, 'load', dumpfile]) -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, 'getprinc', realm.user_princ]) -if 'Expiration date: [never]' not in out or 'MKey: vno 1' not in out: - fail('Principal has wrong value after load') -out = realm.run([kadminl, 'getpols']) -if 'fred\n' not in out or 'barney\n' not in out: - fail('Missing policy after load') -realm.run([kadminl, 'getpol', 'compat'], - expected_msg='Number of old keys kept: 5') -realm.run([kadminl, 'getpol', 'barney'], - expected_msg='Number of old keys kept: 1') - -# Dump/load again, and make sure everything is still there. -mark('second reload') -realm.run([kdb5_util, 'dump', dumpfile]) -realm.run([kdb5_util, 'load', dumpfile]) -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 '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') -srcdump = os.path.join(srcdumpdir, 'dump') -srcdump_r18 = os.path.join(srcdumpdir, 'dump.r18') -srcdump_r13 = os.path.join(srcdumpdir, 'dump.r13') -srcdump_b7 = os.path.join(srcdumpdir, 'dump.b7') -srcdump_ov = os.path.join(srcdumpdir, 'dump.ov') - -# Load a dump file from the source directory. -realm.run([kdb5_util, 'destroy', '-f']) -realm.run([kdb5_util, 'load', srcdump]) -realm.run([kdb5_util, 'stash', '-P', 'master']) - def dump_compare(realm, opt, srcfile): mark('dump comparison against %s' % os.path.basename(srcfile)) realm.run([kdb5_util, 'dump'] + opt + [dumpfile]) if not cmp(srcfile, dumpfile, False): fail('Dump output does not match %s' % srcfile) -# Dump the resulting DB in each non-iprop format and compare with -# expected outputs. -dump_compare(realm, [], srcdump) -dump_compare(realm, ['-r18'], srcdump_r18) -dump_compare(realm, ['-r13'], srcdump_r13) -dump_compare(realm, ['-b7'], srcdump_b7) -dump_compare(realm, ['-ov'], srcdump_ov) def load_dump_check_compare(realm, opt, srcfile): mark('load check from %s' % os.path.basename(srcfile)) @@ -89,15 +19,85 @@ def load_dump_check_compare(realm, opt, srcfile): realm.run([kadminl, 'getpols'], expected_msg='testpol') dump_compare(realm, opt, srcfile) -# Load each format of dump, check it, re-dump it, and compare. -load_dump_check_compare(realm, ['-r18'], srcdump_r18) -load_dump_check_compare(realm, ['-r13'], srcdump_r13) -load_dump_check_compare(realm, ['-b7'], srcdump_b7) -# Loading the last (-b7 format) dump won't have loaded the -# per-principal kadm data. Load that incrementally with -ov. -realm.run([kadminl, 'getprinc', 'user'], expected_msg='Policy: [none]') -realm.run([kdb5_util, 'load', '-update', '-ov', srcdump_ov]) -realm.run([kadminl, 'getprinc', 'user'], expected_msg='Policy: testpol') +for realm in multidb_realms(start_kdc=False): + + # Make sure we can dump and load an ordinary database, and that + # principals and policies survive a dump/load cycle. + + realm.run([kadminl, 'addpol', 'fred']) + + # Create a dump file. + dumpfile = os.path.join(realm.testdir, 'dump') + realm.run([kdb5_util, 'dump', dumpfile]) + + # 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\tcompat\t0\t0\t3\t4\t5\t0\t0\t0\t0\n') + f.write('policy\tbarney\t0\t0\t1\t1\t1\t0\t0\t0\t0\t0\t0\t0\t-\t1\t2\t28\t' + 'fd100f5064625f6372656174696f6e404b5242544553542e434f4d00\n') + f.close() + + # Destroy and load the database; check that the policies exist. + # Spot-check principal and policy fields. + mark('reload after dump') + realm.run([kdb5_util, 'destroy', '-f']) + realm.run([kdb5_util, 'load', dumpfile]) + 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, 'getprinc', realm.user_princ]) + if 'Expiration date: [never]' not in out or 'MKey: vno 1' not in out: + fail('Principal has wrong value after load') + out = realm.run([kadminl, 'getpols']) + if 'fred\n' not in out or 'barney\n' not in out: + fail('Missing policy after load') + realm.run([kadminl, 'getpol', 'compat'], + expected_msg='Number of old keys kept: 5') + realm.run([kadminl, 'getpol', 'barney'], + expected_msg='Number of old keys kept: 1') + + # Dump/load again, and make sure everything is still there. + mark('second reload') + realm.run([kdb5_util, 'dump', dumpfile]) + realm.run([kdb5_util, 'load', dumpfile]) + 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 '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') + srcdump = os.path.join(srcdumpdir, 'dump') + srcdump_r18 = os.path.join(srcdumpdir, 'dump.r18') + srcdump_r13 = os.path.join(srcdumpdir, 'dump.r13') + srcdump_b7 = os.path.join(srcdumpdir, 'dump.b7') + srcdump_ov = os.path.join(srcdumpdir, 'dump.ov') + + # Load a dump file from the source directory. + realm.run([kdb5_util, 'destroy', '-f']) + realm.run([kdb5_util, 'load', srcdump]) + realm.run([kdb5_util, 'stash', '-P', 'master']) + + # Dump the resulting DB in each non-iprop format and compare with + # expected outputs. + dump_compare(realm, [], srcdump) + dump_compare(realm, ['-r18'], srcdump_r18) + dump_compare(realm, ['-r13'], srcdump_r13) + dump_compare(realm, ['-b7'], srcdump_b7) + dump_compare(realm, ['-ov'], srcdump_ov) + + # Load each format of dump, check it, re-dump it, and compare. + load_dump_check_compare(realm, ['-r18'], srcdump_r18) + load_dump_check_compare(realm, ['-r13'], srcdump_r13) + load_dump_check_compare(realm, ['-b7'], srcdump_b7) + + # Loading the last (-b7 format) dump won't have loaded the + # per-principal kadm data. Load that incrementally with -ov. + realm.run([kadminl, 'getprinc', 'user'], expected_msg='Policy: [none]') + realm.run([kdb5_util, 'load', '-update', '-ov', srcdump_ov]) + realm.run([kadminl, 'getprinc', 'user'], expected_msg='Policy: testpol') success('Dump/load tests') diff --git a/src/tests/t_iprop.py b/src/tests/t_iprop.py index 13ef1cab02..54b4098a85 100755 --- a/src/tests/t_iprop.py +++ b/src/tests/t_iprop.py @@ -129,340 +129,354 @@ conf_slave2 = {'realms': {'$realm': {'iprop_slave_poll': '600', conf_foo = {'libdefaults': {'default_realm': 'FOO'}, 'domain_realm': {hostname: 'FOO'}} - -realm = K5Realm(kdc_conf=conf, create_user=False, start_kadmind=True) -slave1 = realm.special_env('slave1', True, kdc_conf=conf_slave1) -slave1m = realm.special_env('slave1m', True, krb5_conf=conf_foo, - kdc_conf=conf_slave1m) -slave2 = realm.special_env('slave2', True, kdc_conf=conf_slave2) - -# A default_realm and domain_realm that do not match the KDC's realm. -# The FOO realm iprop_logfile setting is needed to run kproplog during -# a slave3 test, since kproplog has no realm option. conf_slave3 = {'realms': {'$realm': {'iprop_slave_poll': '600', 'iprop_logfile': '$testdir/ulog.slave3', 'iprop_port': '$port8'}, 'FOO': {'iprop_logfile': '$testdir/ulog.slave3'}}, 'dbmodules': {'db': {'database_name': '$testdir/db.slave3'}}} -slave3 = realm.special_env('slave3', True, krb5_conf=conf_foo, - kdc_conf=conf_slave3) -# A default realm and a domain realm map that differ. krb5_conf_slave4 = {'domain_realm': {hostname: 'FOO'}} conf_slave4 = {'realms': {'$realm': {'iprop_slave_poll': '600', 'iprop_logfile': '$testdir/ulog.slave4', 'iprop_port': '$port8'}}, 'dbmodules': {'db': {'database_name': '$testdir/db.slave4'}}} -slave4 = realm.special_env('slave4', True, krb5_conf=krb5_conf_slave4, - kdc_conf=conf_slave4) - -# Define some principal names. pr3 is long enough to cause internal -# reallocs, but not long enough to grow the basic ulog entry size. -pr1 = 'wakawaka@' + realm.realm -pr2 = 'w@' + realm.realm -c = 'chocolate-flavored-school-bus' -cs = c + '/' -pr3 = (cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + c + - '@' + realm.realm) - -# Create the kpropd ACL file. -acl_file = os.path.join(realm.testdir, 'kpropd-acl') -acl = open(acl_file, 'w') -acl.write(realm.host_princ + '\n') -acl.close() - -ulog = os.path.join(realm.testdir, 'db.ulog') -if not os.path.exists(ulog): - fail('update log not created: ' + ulog) - -# Create the principal used to authenticate kpropd to kadmind. -kiprop_princ = 'kiprop/' + hostname -realm.extract_keytab(kiprop_princ, realm.keytab) - -# Create the initial slave databases. -dumpfile = os.path.join(realm.testdir, 'dump') -realm.run([kdb5_util, 'dump', dumpfile]) -realm.run([kdb5_util, 'load', dumpfile], slave1) -realm.run([kdb5_util, 'load', dumpfile], slave2) -realm.run([kdb5_util, '-r', realm.realm, 'load', dumpfile], slave3) -realm.run([kdb5_util, 'load', dumpfile], slave4) - -# Reinitialize the master ulog so we know exactly what to expect in -# it. -realm.run([kproplog, '-R']) -check_ulog(1, 1, 1, [None]) - -# Make some changes to the master DB. -realm.addprinc(pr1) -realm.addprinc(pr3) -realm.addprinc(pr2) -realm.run([kadminl, 'modprinc', '-allow_tix', pr2]) -realm.run([kadminl, 'modprinc', '+allow_tix', pr2]) -check_ulog(6, 1, 6, [None, pr1, pr3, pr2, pr2, pr2]) - -# Start kpropd for slave1 and get a full dump from master. -mark('propagate M->1 full') -kpropd1 = realm.start_kpropd(slave1, ['-d']) -wait_for_prop(kpropd1, True, 1, 6) -out = realm.run([kadminl, 'listprincs'], env=slave1) -if pr1 not in out or pr2 not in out or pr3 not in out: - fail('slave1 does not have all principals from master') -check_ulog(1, 6, 6, [None], slave1) - -# Make a change and check that it propagates incrementally. -mark('propagate M->1 incremental') -realm.run([kadminl, 'modprinc', '-allow_tix', pr2]) -check_ulog(7, 1, 7, [None, pr1, pr3, pr2, pr2, pr2, pr2]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, False, 6, 7) -check_ulog(2, 6, 7, [None, pr2], slave1) -realm.run([kadminl, 'getprinc', pr2], env=slave1, - expected_msg='Attributes: DISALLOW_ALL_TIX') - -# Start kadmind -proponly for slave1. (Use the slave1m environment -# which defines iprop_port to $port8.) -slave1_out_dump_path = os.path.join(realm.testdir, 'dump.slave1.out') -slave2_in_dump_path = os.path.join(realm.testdir, 'dump.slave2.in') -slave2_kprop_port = str(realm.portbase + 9) -realm.start_server([kadmind, '-r', realm.realm, '-nofork', '-proponly', '-W', - '-p', kdb5_util, '-K', kprop, '-k', slave2_kprop_port, - '-F', slave1_out_dump_path], 'starting...', slave1m) - -# Test similar default_realm and domain_realm map settings with -r realm. -mark('propagate 1->3 full') -slave3_in_dump_path = os.path.join(realm.testdir, 'dump.slave3.in') -kpropd3 = realm.start_server([kpropd, '-d', '-D', '-r', realm.realm, '-P', - slave2_kprop_port, '-f', slave3_in_dump_path, - '-p', kdb5_util, '-a', acl_file, '-A', hostname], - 'ready', slave3) -wait_for_prop(kpropd3, True, 1, 7) -out = realm.run([kadminl, '-r', realm.realm, 'listprincs'], env=slave3) -if pr1 not in out or pr2 not in out or pr3 not in out: - fail('slave3 does not have all principals from slave1') -check_ulog(1, 7, 7, [None], env=slave3) - -# Test an incremental propagation for the kpropd -r case. -mark('propagate M->1->3 incremental') -realm.run([kadminl, 'modprinc', '-maxlife', '20 minutes', pr1]) -check_ulog(8, 1, 8, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, False, 7, 8) -check_ulog(3, 6, 8, [None, pr2, pr1], slave1) -realm.run([kadminl, 'getprinc', pr1], env=slave1, - expected_msg='Maximum ticket life: 0 days 00:20:00') -kpropd3.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd3, False, 7, 8) -check_ulog(2, 7, 8, [None, pr1], slave3) -realm.run([kadminl, '-r', realm.realm, 'getprinc', pr1], env=slave3, - expected_msg='Maximum ticket life: 0 days 00:20:00') -stop_daemon(kpropd3) - -# Test dissimilar default_realm and domain_realm map settings (no -r realm). -mark('propagate 1->4 full') -slave4_in_dump_path = os.path.join(realm.testdir, 'dump.slave4.in') -kpropd4 = realm.start_server([kpropd, '-d', '-D', '-P', slave2_kprop_port, - '-f', slave4_in_dump_path, '-p', kdb5_util, - '-a', acl_file, '-A', hostname], 'ready', slave4) -wait_for_prop(kpropd4, True, 1, 8) -out = realm.run([kadminl, 'listprincs'], env=slave4) -if pr1 not in out or pr2 not in out or pr3 not in out: - fail('slave4 does not have all principals from slave1') -stop_daemon(kpropd4) - -# Start kpropd for slave2. The -A option isn't needed since we're -# talking to the same host as master (we specify it anyway to exercise -# the code), but slave2 defines iprop_port to $port8 so it will talk -# to slave1. Get a full dump from slave1. -mark('propagate 1->2 full') -kpropd2 = realm.start_server([kpropd, '-d', '-D', '-P', slave2_kprop_port, - '-f', slave2_in_dump_path, '-p', kdb5_util, - '-a', acl_file, '-A', hostname], 'ready', slave2) -wait_for_prop(kpropd2, True, 1, 8) -check_ulog(2, 7, 8, [None, pr1], slave2) -out = realm.run([kadminl, 'listprincs'], env=slave1) -if pr1 not in out or pr2 not in out or pr3 not in out: - fail('slave2 does not have all principals from slave1') - -# Make another change and check that it propagates incrementally to -# both slaves. -mark('propagate M->1->2 incremental') -realm.run([kadminl, 'modprinc', '-maxrenewlife', '22 hours', pr1]) -check_ulog(9, 1, 9, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1, pr1]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, False, 8, 9) -check_ulog(4, 6, 9, [None, pr2, pr1, pr1], slave1) -realm.run([kadminl, 'getprinc', pr1], env=slave1, - expected_msg='Maximum renewable life: 0 days 22:00:00\n') -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, False, 8, 9) -check_ulog(3, 7, 9, [None, pr1, pr1], slave2) -realm.run([kadminl, 'getprinc', pr1], env=slave2, - expected_msg='Maximum renewable life: 0 days 22:00:00\n') - -# Reset the ulog on slave1 to force a full resync from master. The -# resync will use the old dump file and then propagate changes. -# slave2 should still be in sync with slave1 after the resync, so make -# sure it doesn't take a full resync. -mark('propagate M->1->2 full') -realm.run([kproplog, '-R'], slave1) -check_ulog(1, 1, 1, [None], slave1) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, True, 1, 9) -check_ulog(4, 6, 9, [None, pr2, pr1, pr1], slave1) -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, False, 9, 9) -check_ulog(3, 7, 9, [None, pr1, pr1], slave2) - -# Make another change and check that it propagates incrementally to -# both slaves. -mark('propagate M->1->2 incremental (after reset)') -realm.run([kadminl, 'modprinc', '+allow_tix', pr2]) -check_ulog(10, 1, 10, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1, pr1, pr2]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, False, 9, 10) -check_ulog(5, 6, 10, [None, pr2, pr1, pr1, pr2], slave1) -realm.run([kadminl, 'getprinc', pr2], env=slave1, expected_msg='Attributes:\n') -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, False, 9, 10) -check_ulog(4, 7, 10, [None, pr1, pr1, pr2], slave2) -realm.run([kadminl, 'getprinc', pr2], env=slave2, expected_msg='Attributes:\n') - -# Create a policy and check that it propagates via full resync. -mark('propagate M->1->2 full (new policy)') -realm.run([kadminl, 'addpol', '-minclasses', '2', 'testpol']) -check_ulog(1, 1, 1, [None]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, True, 10, 1) -check_ulog(1, 1, 1, [None], slave1) -realm.run([kadminl, 'getpol', 'testpol'], env=slave1, - expected_msg='Minimum number of password character classes: 2') -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, True, 10, 1) -check_ulog(1, 1, 1, [None], slave2) -realm.run([kadminl, 'getpol', 'testpol'], env=slave2, - expected_msg='Minimum number of password character classes: 2') - -# Modify the policy and test that it also propagates via full resync. -mark('propagate M->1->2 full (policy change)') -realm.run([kadminl, 'modpol', '-minlength', '17', 'testpol']) -check_ulog(1, 1, 1, [None]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, True, 1, 1) -check_ulog(1, 1, 1, [None], slave1) -realm.run([kadminl, 'getpol', 'testpol'], env=slave1, - expected_msg='Minimum password length: 17') -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, True, 1, 1) -check_ulog(1, 1, 1, [None], slave2) -realm.run([kadminl, 'getpol', 'testpol'], env=slave2, - expected_msg='Minimum password length: 17') - -# Delete the policy and test that it propagates via full resync. -mark('propgate M->1->2 full (policy delete)') -realm.run([kadminl, 'delpol', 'testpol']) -check_ulog(1, 1, 1, [None]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, True, 1, 1) -check_ulog(1, 1, 1, [None], slave1) -realm.run([kadminl, 'getpol', 'testpol'], env=slave1, expected_code=1, - expected_msg='Policy does not exist') -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, True, 1, 1) -check_ulog(1, 1, 1, [None], slave2) -realm.run([kadminl, 'getpol', 'testpol'], env=slave2, expected_code=1, - expected_msg='Policy does not exist') - -# Modify a principal on the master and test that it propagates incrementally. -mark('propagate M->1->2 incremental (after policy changes)') -realm.run([kadminl, 'modprinc', '-maxlife', '10 minutes', pr1]) -check_ulog(2, 1, 2, [None, pr1]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, False, 1, 2) -check_ulog(2, 1, 2, [None, pr1], slave1) -realm.run([kadminl, 'getprinc', pr1], env=slave1, - expected_msg='Maximum ticket life: 0 days 00:10:00') -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, False, 1, 2) -check_ulog(2, 1, 2, [None, pr1], slave2) -realm.run([kadminl, 'getprinc', pr1], env=slave2, - expected_msg='Maximum ticket life: 0 days 00:10:00') - -# Delete a principal and test that it propagates incrementally. -mark('propagate M->1->2 incremental (princ delete)') -realm.run([kadminl, 'delprinc', pr3]) -check_ulog(3, 1, 3, [None, pr1, pr3]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, False, 2, 3) -check_ulog(3, 1, 3, [None, pr1, pr3], slave1) -realm.run([kadminl, 'getprinc', pr3], env=slave1, expected_code=1, - expected_msg='Principal does not exist') -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, False, 2, 3) -check_ulog(3, 1, 3, [None, pr1, pr3], slave2) -realm.run([kadminl, 'getprinc', pr3], env=slave2, expected_code=1, - expected_msg='Principal does not exist') - -# Rename a principal and test that it propagates incrementally. -mark('propagate M->1->2 incremental (princ rename)') -renpr = "quacked@" + realm.realm -realm.run([kadminl, 'renprinc', pr1, renpr]) -check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, False, 3, 6) -check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr], slave1) -realm.run([kadminl, 'getprinc', pr1], env=slave1, expected_code=1, - expected_msg='Principal does not exist') -realm.run([kadminl, 'getprinc', renpr], env=slave1) -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, False, 3, 6) -check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr], slave2) -realm.run([kadminl, 'getprinc', pr1], env=slave2, expected_code=1, - expected_msg='Principal does not exist') -realm.run([kadminl, 'getprinc', renpr], env=slave2) - -pr1 = renpr - -# Reset the ulog on the master to force a full resync. -mark('propagate M->1->2 full (ulog reset)') -realm.run([kproplog, '-R']) -check_ulog(1, 1, 1, [None]) -kpropd1.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd1, True, 6, 1) -check_ulog(1, 1, 1, [None], slave1) -kpropd2.send_signal(signal.SIGUSR1) -wait_for_prop(kpropd2, True, 6, 1) -check_ulog(1, 1, 1, [None], slave2) - -# Stop the kprop daemons so we can test kpropd -t. -stop_daemon(kpropd1) -stop_daemon(kpropd2) -mark('kpropd -t') - -# Test the case where no updates are needed. -out = realm.run_kpropd_once(slave1, ['-d']) -if 'KDC is synchronized' not in out: - fail('Expected synchronized from kpropd -t') -check_ulog(1, 1, 1, [None], slave1) - -# Make a change on the master and fetch it incrementally. -realm.run([kadminl, 'modprinc', '-maxlife', '5 minutes', pr1]) -check_ulog(2, 1, 2, [None, pr1]) -out = realm.run_kpropd_once(slave1, ['-d']) -if 'Got incremental updates (sno=2 ' not in out: - fail('Expected full dump and synchronized from kpropd -t') -check_ulog(2, 1, 2, [None, pr1], slave1) -realm.run([kadminl, 'getprinc', pr1], env=slave1, - expected_msg='Maximum ticket life: 0 days 00:05:00') - -# Propagate a policy change via full resync. -realm.run([kadminl, 'addpol', '-minclasses', '3', 'testpol']) -check_ulog(1, 1, 1, [None]) -out = realm.run_kpropd_once(slave1, ['-d']) -if ('Full propagation transfer finished' not in out or - 'KDC is synchronized' not in out): - fail('Expected full dump and synchronized from kpropd -t') -check_ulog(1, 1, 1, [None], slave1) -realm.run([kadminl, 'getpol', 'testpol'], env=slave1, - expected_msg='Minimum number of password character classes: 3') + +for realm in multidb_realms(kdc_conf=conf, create_user=False, + start_kadmind=True): + slave1 = realm.special_env('slave1', True, kdc_conf=conf_slave1) + slave1m = realm.special_env('slave1m', True, krb5_conf=conf_foo, + kdc_conf=conf_slave1m) + slave2 = realm.special_env('slave2', True, kdc_conf=conf_slave2) + + # A default_realm and domain_realm that do not match the KDC's + # realm. The FOO realm iprop_logfile setting is needed to run + # kproplog during a slave3 test, since kproplog has no realm + # option. + slave3 = realm.special_env('slave3', True, krb5_conf=conf_foo, + kdc_conf=conf_slave3) + + # A default realm and a domain realm map that differ. + slave4 = realm.special_env('slave4', True, krb5_conf=krb5_conf_slave4, + kdc_conf=conf_slave4) + + # Define some principal names. pr3 is long enough to cause internal + # reallocs, but not long enough to grow the basic ulog entry size. + pr1 = 'wakawaka@' + realm.realm + pr2 = 'w@' + realm.realm + c = 'chocolate-flavored-school-bus' + cs = c + '/' + pr3 = (cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + c + + '@' + realm.realm) + + # Create the kpropd ACL file. + acl_file = os.path.join(realm.testdir, 'kpropd-acl') + acl = open(acl_file, 'w') + acl.write(realm.host_princ + '\n') + acl.close() + + ulog = os.path.join(realm.testdir, 'db.ulog') + if not os.path.exists(ulog): + fail('update log not created: ' + ulog) + + # Create the principal used to authenticate kpropd to kadmind. + kiprop_princ = 'kiprop/' + hostname + realm.extract_keytab(kiprop_princ, realm.keytab) + + # Create the initial slave databases. + dumpfile = os.path.join(realm.testdir, 'dump') + realm.run([kdb5_util, 'dump', dumpfile]) + realm.run([kdb5_util, 'load', dumpfile], slave1) + realm.run([kdb5_util, 'load', dumpfile], slave2) + realm.run([kdb5_util, '-r', realm.realm, 'load', dumpfile], slave3) + realm.run([kdb5_util, 'load', dumpfile], slave4) + + # Reinitialize the master ulog so we know exactly what to expect in + # it. + realm.run([kproplog, '-R']) + check_ulog(1, 1, 1, [None]) + + # Make some changes to the master DB. + realm.addprinc(pr1) + realm.addprinc(pr3) + realm.addprinc(pr2) + realm.run([kadminl, 'modprinc', '-allow_tix', pr2]) + realm.run([kadminl, 'modprinc', '+allow_tix', pr2]) + check_ulog(6, 1, 6, [None, pr1, pr3, pr2, pr2, pr2]) + + # Start kpropd for slave1 and get a full dump from master. + mark('propagate M->1 full') + kpropd1 = realm.start_kpropd(slave1, ['-d']) + wait_for_prop(kpropd1, True, 1, 6) + out = realm.run([kadminl, 'listprincs'], env=slave1) + if pr1 not in out or pr2 not in out or pr3 not in out: + fail('slave1 does not have all principals from master') + check_ulog(1, 6, 6, [None], slave1) + + # Make a change and check that it propagates incrementally. + mark('propagate M->1 incremental') + realm.run([kadminl, 'modprinc', '-allow_tix', pr2]) + check_ulog(7, 1, 7, [None, pr1, pr3, pr2, pr2, pr2, pr2]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, False, 6, 7) + check_ulog(2, 6, 7, [None, pr2], slave1) + realm.run([kadminl, 'getprinc', pr2], env=slave1, + expected_msg='Attributes: DISALLOW_ALL_TIX') + + # Start kadmind -proponly for slave1. (Use the slave1m environment + # which defines iprop_port to $port8.) + slave1_out_dump_path = os.path.join(realm.testdir, 'dump.slave1.out') + slave2_in_dump_path = os.path.join(realm.testdir, 'dump.slave2.in') + slave2_kprop_port = str(realm.portbase + 9) + kadmind_proponly = realm.start_server([kadmind, '-r', realm.realm, + '-nofork', '-proponly', + '-W', '-p', kdb5_util, + '-K', kprop, '-k', + slave2_kprop_port, + '-F', slave1_out_dump_path], + 'starting...', slave1m) + + # Test similar default_realm and domain_realm map settings with -r realm. + mark('propagate 1->3 full') + slave3_in_dump_path = os.path.join(realm.testdir, 'dump.slave3.in') + kpropd3 = realm.start_server([kpropd, '-d', '-D', '-r', realm.realm, '-P', + slave2_kprop_port, '-f', slave3_in_dump_path, + '-p', kdb5_util, '-a', acl_file, + '-A', hostname], 'ready', slave3) + wait_for_prop(kpropd3, True, 1, 7) + out = realm.run([kadminl, '-r', realm.realm, 'listprincs'], env=slave3) + if pr1 not in out or pr2 not in out or pr3 not in out: + fail('slave3 does not have all principals from slave1') + check_ulog(1, 7, 7, [None], env=slave3) + + # Test an incremental propagation for the kpropd -r case. + mark('propagate M->1->3 incremental') + realm.run([kadminl, 'modprinc', '-maxlife', '20 minutes', pr1]) + check_ulog(8, 1, 8, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, False, 7, 8) + check_ulog(3, 6, 8, [None, pr2, pr1], slave1) + realm.run([kadminl, 'getprinc', pr1], env=slave1, + expected_msg='Maximum ticket life: 0 days 00:20:00') + kpropd3.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd3, False, 7, 8) + check_ulog(2, 7, 8, [None, pr1], slave3) + realm.run([kadminl, '-r', realm.realm, 'getprinc', pr1], env=slave3, + expected_msg='Maximum ticket life: 0 days 00:20:00') + stop_daemon(kpropd3) + + # Test dissimilar default_realm and domain_realm map settings (no + # -r realm). + mark('propagate 1->4 full') + slave4_in_dump_path = os.path.join(realm.testdir, 'dump.slave4.in') + kpropd4 = realm.start_server([kpropd, '-d', '-D', '-P', slave2_kprop_port, + '-f', slave4_in_dump_path, '-p', kdb5_util, + '-a', acl_file, '-A', hostname], 'ready', + slave4) + wait_for_prop(kpropd4, True, 1, 8) + out = realm.run([kadminl, 'listprincs'], env=slave4) + if pr1 not in out or pr2 not in out or pr3 not in out: + fail('slave4 does not have all principals from slave1') + stop_daemon(kpropd4) + + # Start kpropd for slave2. The -A option isn't needed since we're + # talking to the same host as master (we specify it anyway to + # exercise the code), but slave2 defines iprop_port to $port8 so + # it will talk to slave1. Get a full dump from slave1. + mark('propagate 1->2 full') + kpropd2 = realm.start_server([kpropd, '-d', '-D', '-P', slave2_kprop_port, + '-f', slave2_in_dump_path, '-p', kdb5_util, + '-a', acl_file, '-A', hostname], 'ready', + slave2) + wait_for_prop(kpropd2, True, 1, 8) + check_ulog(2, 7, 8, [None, pr1], slave2) + out = realm.run([kadminl, 'listprincs'], env=slave1) + if pr1 not in out or pr2 not in out or pr3 not in out: + fail('slave2 does not have all principals from slave1') + + # Make another change and check that it propagates incrementally + # to both slaves. + mark('propagate M->1->2 incremental') + realm.run([kadminl, 'modprinc', '-maxrenewlife', '22 hours', pr1]) + check_ulog(9, 1, 9, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1, pr1]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, False, 8, 9) + check_ulog(4, 6, 9, [None, pr2, pr1, pr1], slave1) + realm.run([kadminl, 'getprinc', pr1], env=slave1, + expected_msg='Maximum renewable life: 0 days 22:00:00\n') + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, False, 8, 9) + check_ulog(3, 7, 9, [None, pr1, pr1], slave2) + realm.run([kadminl, 'getprinc', pr1], env=slave2, + expected_msg='Maximum renewable life: 0 days 22:00:00\n') + + # Reset the ulog on slave1 to force a full resync from master. + # The resync will use the old dump file and then propagate + # changes. slave2 should still be in sync with slave1 after the + # resync, so make sure it doesn't take a full resync. + mark('propagate M->1->2 full') + realm.run([kproplog, '-R'], slave1) + check_ulog(1, 1, 1, [None], slave1) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, True, 1, 9) + check_ulog(4, 6, 9, [None, pr2, pr1, pr1], slave1) + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, False, 9, 9) + check_ulog(3, 7, 9, [None, pr1, pr1], slave2) + + # Make another change and check that it propagates incrementally to + # both slaves. + mark('propagate M->1->2 incremental (after reset)') + realm.run([kadminl, 'modprinc', '+allow_tix', pr2]) + check_ulog(10, 1, 10, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1, pr1, pr2]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, False, 9, 10) + check_ulog(5, 6, 10, [None, pr2, pr1, pr1, pr2], slave1) + realm.run([kadminl, 'getprinc', pr2], env=slave1, + expected_msg='Attributes:\n') + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, False, 9, 10) + check_ulog(4, 7, 10, [None, pr1, pr1, pr2], slave2) + realm.run([kadminl, 'getprinc', pr2], env=slave2, + expected_msg='Attributes:\n') + + # Create a policy and check that it propagates via full resync. + mark('propagate M->1->2 full (new policy)') + realm.run([kadminl, 'addpol', '-minclasses', '2', 'testpol']) + check_ulog(1, 1, 1, [None]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, True, 10, 1) + check_ulog(1, 1, 1, [None], slave1) + realm.run([kadminl, 'getpol', 'testpol'], env=slave1, + expected_msg='Minimum number of password character classes: 2') + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, True, 10, 1) + check_ulog(1, 1, 1, [None], slave2) + realm.run([kadminl, 'getpol', 'testpol'], env=slave2, + expected_msg='Minimum number of password character classes: 2') + + # Modify the policy and test that it also propagates via full resync. + mark('propagate M->1->2 full (policy change)') + realm.run([kadminl, 'modpol', '-minlength', '17', 'testpol']) + check_ulog(1, 1, 1, [None]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, True, 1, 1) + check_ulog(1, 1, 1, [None], slave1) + realm.run([kadminl, 'getpol', 'testpol'], env=slave1, + expected_msg='Minimum password length: 17') + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, True, 1, 1) + check_ulog(1, 1, 1, [None], slave2) + realm.run([kadminl, 'getpol', 'testpol'], env=slave2, + expected_msg='Minimum password length: 17') + + # Delete the policy and test that it propagates via full resync. + mark('propgate M->1->2 full (policy delete)') + realm.run([kadminl, 'delpol', 'testpol']) + check_ulog(1, 1, 1, [None]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, True, 1, 1) + check_ulog(1, 1, 1, [None], slave1) + realm.run([kadminl, 'getpol', 'testpol'], env=slave1, expected_code=1, + expected_msg='Policy does not exist') + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, True, 1, 1) + check_ulog(1, 1, 1, [None], slave2) + realm.run([kadminl, 'getpol', 'testpol'], env=slave2, expected_code=1, + expected_msg='Policy does not exist') + + # Modify a principal on the master and test that it propagates + # incrementally. + mark('propagate M->1->2 incremental (after policy changes)') + realm.run([kadminl, 'modprinc', '-maxlife', '10 minutes', pr1]) + check_ulog(2, 1, 2, [None, pr1]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, False, 1, 2) + check_ulog(2, 1, 2, [None, pr1], slave1) + realm.run([kadminl, 'getprinc', pr1], env=slave1, + expected_msg='Maximum ticket life: 0 days 00:10:00') + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, False, 1, 2) + check_ulog(2, 1, 2, [None, pr1], slave2) + realm.run([kadminl, 'getprinc', pr1], env=slave2, + expected_msg='Maximum ticket life: 0 days 00:10:00') + + # Delete a principal and test that it propagates incrementally. + mark('propagate M->1->2 incremental (princ delete)') + realm.run([kadminl, 'delprinc', pr3]) + check_ulog(3, 1, 3, [None, pr1, pr3]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, False, 2, 3) + check_ulog(3, 1, 3, [None, pr1, pr3], slave1) + realm.run([kadminl, 'getprinc', pr3], env=slave1, expected_code=1, + expected_msg='Principal does not exist') + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, False, 2, 3) + check_ulog(3, 1, 3, [None, pr1, pr3], slave2) + realm.run([kadminl, 'getprinc', pr3], env=slave2, expected_code=1, + expected_msg='Principal does not exist') + + # Rename a principal and test that it propagates incrementally. + mark('propagate M->1->2 incremental (princ rename)') + renpr = "quacked@" + realm.realm + realm.run([kadminl, 'renprinc', pr1, renpr]) + check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, False, 3, 6) + check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr], slave1) + realm.run([kadminl, 'getprinc', pr1], env=slave1, expected_code=1, + expected_msg='Principal does not exist') + realm.run([kadminl, 'getprinc', renpr], env=slave1) + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, False, 3, 6) + check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr], slave2) + realm.run([kadminl, 'getprinc', pr1], env=slave2, expected_code=1, + expected_msg='Principal does not exist') + realm.run([kadminl, 'getprinc', renpr], env=slave2) + + pr1 = renpr + + # Reset the ulog on the master to force a full resync. + mark('propagate M->1->2 full (ulog reset)') + realm.run([kproplog, '-R']) + check_ulog(1, 1, 1, [None]) + kpropd1.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd1, True, 6, 1) + check_ulog(1, 1, 1, [None], slave1) + kpropd2.send_signal(signal.SIGUSR1) + wait_for_prop(kpropd2, True, 6, 1) + check_ulog(1, 1, 1, [None], slave2) + + # Stop the kprop daemons so we can test kpropd -t. + realm.stop_kpropd(kpropd1) + stop_daemon(kpropd2) + stop_daemon(kadmind_proponly) + mark('kpropd -t') + + # Test the case where no updates are needed. + out = realm.run_kpropd_once(slave1, ['-d']) + if 'KDC is synchronized' not in out: + fail('Expected synchronized from kpropd -t') + check_ulog(1, 1, 1, [None], slave1) + + # Make a change on the master and fetch it incrementally. + realm.run([kadminl, 'modprinc', '-maxlife', '5 minutes', pr1]) + check_ulog(2, 1, 2, [None, pr1]) + out = realm.run_kpropd_once(slave1, ['-d']) + if 'Got incremental updates (sno=2 ' not in out: + fail('Expected full dump and synchronized from kpropd -t') + check_ulog(2, 1, 2, [None, pr1], slave1) + realm.run([kadminl, 'getprinc', pr1], env=slave1, + expected_msg='Maximum ticket life: 0 days 00:05:00') + + # Propagate a policy change via full resync. + realm.run([kadminl, 'addpol', '-minclasses', '3', 'testpol']) + check_ulog(1, 1, 1, [None]) + out = realm.run_kpropd_once(slave1, ['-d']) + if ('Full propagation transfer finished' not in out or + 'KDC is synchronized' not in out): + fail('Expected full dump and synchronized from kpropd -t') + check_ulog(1, 1, 1, [None], slave1) + realm.run([kadminl, 'getpol', 'testpol'], env=slave1, + expected_msg='Minimum number of password character classes: 3') success('iprop tests') diff --git a/src/tests/t_kdb.py b/src/tests/t_kdb.py index 8438a45aa5..78de223d14 100755 --- a/src/tests/t_kdb.py +++ b/src/tests/t_kdb.py @@ -3,9 +3,9 @@ from k5test import * import time from itertools import imap -# Run kdbtest against the BDB module. -realm = K5Realm(create_kdb=False) -realm.run(['./kdbtest']) +# Run kdbtest against the non-LDAP KDB modules. +for realm in multidb_realms(create_kdb=False): + realm.run(['./kdbtest']) # Set up an OpenLDAP test server if we can. diff --git a/src/tests/t_kdb_locking.py b/src/tests/t_kdb_locking.py index aac0a220f4..ea32ad7174 100755 --- a/src/tests/t_kdb_locking.py +++ b/src/tests/t_kdb_locking.py @@ -13,7 +13,7 @@ import os from k5test import * p = 'foo' -realm = K5Realm(create_user=False) +realm = K5Realm(create_user=False, bdb_only=True) realm.addprinc(p, p) kadm5_lock = os.path.join(realm.testdir, 'db.kadm5.lock') diff --git a/src/tests/t_policy.py b/src/tests/t_policy.py index 9d92ebdc87..5c9e87c561 100755 --- a/src/tests/t_policy.py +++ b/src/tests/t_policy.py @@ -60,34 +60,36 @@ realm.run([kadminl, 'cpw', '-pw', 'aa', 'pwuser']) # Test basic password lockout support. mark('password lockout') -realm.run([kadminl, 'addpol', '-maxfailure', '2', '-failurecountinterval', - '5m', 'lockout']) -realm.run([kadminl, 'modprinc', '+requires_preauth', '-policy', 'lockout', - 'user']) - -# kinit twice with the wrong password. -realm.run([kinit, realm.user_princ], input='wrong\n', expected_code=1, - expected_msg='Password incorrect while getting initial credentials') -realm.run([kinit, realm.user_princ], input='wrong\n', expected_code=1, - expected_msg='Password incorrect while getting initial credentials') - -# Now the account should be locked out. -m = 'Client\'s credentials have been revoked while getting initial credentials' -realm.run([kinit, realm.user_princ], expected_code=1, expected_msg=m) - -# Check that modprinc -unlock allows a further attempt. -realm.run([kadminl, 'modprinc', '-unlock', 'user']) -realm.kinit(realm.user_princ, password('user')) - -# Make sure a nonexistent policy reference doesn't prevent authentication. -realm.run([kadminl, 'delpol', 'lockout']) -realm.kinit(realm.user_princ, password('user')) +realm.stop() +for realm in multidb_realms(create_host=False): + realm.run([kadminl, 'addpol', '-maxfailure', '2', '-failurecountinterval', + '5m', 'lockout']) + realm.run([kadminl, 'modprinc', '+requires_preauth', '-policy', 'lockout', + 'user']) + + # kinit twice with the wrong password. + msg = 'Password incorrect while getting initial credentials' + realm.run([kinit, realm.user_princ], input='wrong\n', expected_code=1, + expected_msg=msg) + realm.run([kinit, realm.user_princ], input='wrong\n', expected_code=1, + expected_msg=msg) + + # Now the account should be locked out. + msg = 'credentials have been revoked while getting initial credentials' + realm.run([kinit, realm.user_princ], expected_code=1, expected_msg=msg) + + # Check that modprinc -unlock allows a further attempt. + realm.run([kadminl, 'modprinc', '-unlock', 'user']) + realm.kinit(realm.user_princ, password('user')) + + # Make sure a nonexistent policy reference doesn't prevent authentication. + realm.run([kadminl, 'delpol', 'lockout']) + realm.kinit(realm.user_princ, password('user')) # Regression test for issue #7099: databases created prior to krb5 1.3 have # multiple history keys, and kadmin prior to 1.7 didn't necessarily use the # first one to create history entries. mark('#7099 regression test') -realm.stop() realm = K5Realm(start_kdc=False) # Create a history principal with two keys. realm.run(['./hist', 'make']) diff --git a/src/tests/t_unlockiter.py b/src/tests/t_unlockiter.py index 2a438e99ab..60ed77ce36 100755 --- a/src/tests/t_unlockiter.py +++ b/src/tests/t_unlockiter.py @@ -3,7 +3,8 @@ from k5test import * # Default KDB iteration is locked. Expect write lock failure unless # unlocked iteration is explicitly requested. -realm = K5Realm(create_user=False, create_host=False, start_kdc=False) +realm = K5Realm(create_user=False, create_host=False, start_kdc=False, + bdb_only=True) realm.run(['./unlockiter'], expected_code=1) realm.run(['./unlockiter', '-u']) realm.run(['./unlockiter', '-l'], expected_code=1) @@ -11,6 +12,7 @@ realm.run(['./unlockiter', '-l'], expected_code=1) # Set default to unlocked iteration. Only explicitly requested locked # iteration should block the write lock. realm = K5Realm(create_user=False, create_host=False, start_kdc=False, + bdb_only=True, krb5_conf={'dbmodules': {'db': {'unlockiter': 'true'}}}) realm.run(['./unlockiter']) realm.run(['./unlockiter', '-u']) diff --git a/src/util/k5test.py b/src/util/k5test.py index bc32877a71..8d635afedd 100644 --- a/src/util/k5test.py +++ b/src/util/k5test.py @@ -118,6 +118,9 @@ keyword arguments: * get_creds=False: Don't get user credentials. +* bdb_only=True: Use the DB2 KDB module even if K5TEST_LMDB is set in + the environment. + Scripts may use the following functions and variables: * fail(message): Display message (plus leading marker and trailing @@ -165,6 +168,12 @@ Scripts may use the following functions and variables: honored. If keywords contains krb5_conf and/or kdc_conf fragments, they will be merged with the default and per-pass specifications. +* multidb_realms(**keywords): Yields a realm for multiple DB modules. + Currently DB2 and LMDB are included. Ideally LDAP would be + included, but setting up a test LDAP server currently requires a + one-second delay, so all LDAP tests are currently confined to + t_kdb.py. keywords may contain any K5Realm initializer. + * cross_realms(num, xtgts=None, args=None, **keywords): This function returns a list of num realms, where each realm's configuration knows how to contact all of the realms. By default, each realm will @@ -390,6 +399,8 @@ def fail(msg): sys.stdout.write(_last_cmd_output) if _current_pass: print "*** Failed in test pass:", _current_pass + if _current_db: + print "*** Failed with db:", _current_db sys.exit(1) @@ -810,8 +821,9 @@ class K5Realm(object): krb5_conf=None, kdc_conf=None, create_kdb=True, krbtgt_keysalt=None, create_user=True, get_creds=True, create_host=True, start_kdc=True, start_kadmind=False, - start_kpropd=False): + start_kpropd=False, bdb_only=False): global hostname, _default_krb5_conf, _default_kdc_conf + global _lmdb_kdc_conf, _current_db self.realm = realm self.testdir = os.path.join(os.getcwd(), testdir) @@ -826,7 +838,11 @@ class K5Realm(object): self.ccache = os.path.join(self.testdir, 'ccache') self.kadmin_ccache = os.path.join(self.testdir, 'kadmin_ccache') self._krb5_conf = _cfg_merge(_default_krb5_conf, krb5_conf) - self._kdc_conf = _cfg_merge(_default_kdc_conf, kdc_conf) + base_kdc_conf = _default_kdc_conf + if (os.getenv('K5TEST_LMDB') is not None and + not bdb_only and not _current_db): + base_kdc_conf = _cfg_merge(base_kdc_conf, _lmdb_kdc_conf) + self._kdc_conf = _cfg_merge(base_kdc_conf, kdc_conf) self._kdc_proc = None self._kadmind_proc = None self._kpropd_procs = [] @@ -1008,6 +1024,10 @@ class K5Realm(object): self._kpropd_procs.append(proc) return proc + def stop_kpropd(self, proc): + stop_daemon(proc) + self._kpropd_procs.remove(proc) + def run_kpropd_once(self, env, args=[]): return self.run(self._kpropd_args() + ['-t'] + args, env=env) @@ -1102,6 +1122,20 @@ def multipass_realms(**keywords): _current_pass = None +def multidb_realms(**keywords): + global _current_db, _dbpasses + caller_kdc_conf = keywords.get('kdc_conf') + for p in _dbpasses: + (name, kdc_conf) = p + output('*** Using DB type %s\n' % name) + keywords['kdc_conf'] = _cfg_merge(kdc_conf, caller_kdc_conf) + _current_db = name + realm = K5Realm(**keywords) + yield realm + realm.stop() + _current_db = None + + def cross_realms(num, xtgts=None, args=None, **keywords): # Build keyword args for each realm. realm_args = [] @@ -1198,6 +1232,10 @@ _default_kdc_conf = { 'default': 'FILE:$testdir/others.log'}} +_lmdb_kdc_conf = {'dbmodules': {'db': {'db_library': 'klmdb', + 'nosync': 'true'}}} + + # A pass is a tuple of: name, krbtgt_keysalt, krb5_conf, kdc_conf. _passes = [ # No special settings; exercises AES256. @@ -1282,6 +1320,7 @@ _passes = [ _success = False _current_pass = None +_current_db = None _daemons = [] _parse_args() atexit.register(_onexit) @@ -1298,6 +1337,11 @@ runenv = _import_runenv() hostname = _get_hostname() null_input = open(os.devnull, 'r') +# A DB pass is a tuple of: name, kdc_conf. +_dbpasses = [('db2', None)] +if runenv.have_lmdb == 'yes': + _dbpasses.append(('lmdb', _lmdb_kdc_conf)) + krb5kdc = os.path.join(buildtop, 'kdc', 'krb5kdc') kadmind = os.path.join(buildtop, 'kadmin', 'server', 'kadmind') kadmin = os.path.join(buildtop, 'kadmin', 'cli', 'kadmin')