1*7f2fe78bSCy Schubertimport os 2*7f2fe78bSCy Schubertimport re 3*7f2fe78bSCy Schubert 4*7f2fe78bSCy Schubertfrom k5test import * 5*7f2fe78bSCy Schubert 6*7f2fe78bSCy Schubert# On macOS with System Integrity Protection enabled, this script hangs 7*7f2fe78bSCy Schubert# in the wait_for_prop() call after starting the first kpropd process, 8*7f2fe78bSCy Schubert# most likely due to signal restrictions preventing the listening 9*7f2fe78bSCy Schubert# child from informing the parent that a full resync was processed. 10*7f2fe78bSCy Schubertif which('csrutil'): 11*7f2fe78bSCy Schubert out = subprocess.check_output(['csrutil', 'status'], 12*7f2fe78bSCy Schubert universal_newlines=True) 13*7f2fe78bSCy Schubert if 'status: enabled' in out: 14*7f2fe78bSCy Schubert skip_rest('iprop tests', 'System Integrity Protection is enabled') 15*7f2fe78bSCy Schubert 16*7f2fe78bSCy Schubert# Read lines from kpropd output until we are synchronized. Error if 17*7f2fe78bSCy Schubert# full_expected is true and we didn't see a full propagation or vice 18*7f2fe78bSCy Schubert# versa. 19*7f2fe78bSCy Schubertdef wait_for_prop(kpropd, full_expected, expected_old, expected_new): 20*7f2fe78bSCy Schubert output('*** Waiting for sync from kpropd\n') 21*7f2fe78bSCy Schubert full_seen = sleep_seen = False 22*7f2fe78bSCy Schubert old_sno = new_sno = -1 23*7f2fe78bSCy Schubert while True: 24*7f2fe78bSCy Schubert line = kpropd.stdout.readline() 25*7f2fe78bSCy Schubert if line == '': 26*7f2fe78bSCy Schubert fail('kpropd process exited unexpectedly') 27*7f2fe78bSCy Schubert output('kpropd: ' + line) 28*7f2fe78bSCy Schubert 29*7f2fe78bSCy Schubert m = re.match(r'Calling iprop_get_updates_1 \(sno=(\d+) ', line) 30*7f2fe78bSCy Schubert if m: 31*7f2fe78bSCy Schubert if not full_seen: 32*7f2fe78bSCy Schubert old_sno = int(m.group(1)) 33*7f2fe78bSCy Schubert # Also record this as the new sno, in case we get back 34*7f2fe78bSCy Schubert # UPDATE_NIL. 35*7f2fe78bSCy Schubert new_sno = int(m.group(1)) 36*7f2fe78bSCy Schubert 37*7f2fe78bSCy Schubert m = re.match(r'Got incremental updates \(sno=(\d+) ', line) 38*7f2fe78bSCy Schubert if m: 39*7f2fe78bSCy Schubert new_sno = int(m.group(1)) 40*7f2fe78bSCy Schubert 41*7f2fe78bSCy Schubert if 'KDC is synchronized' in line or 'Incremental updates:' in line: 42*7f2fe78bSCy Schubert break 43*7f2fe78bSCy Schubert 44*7f2fe78bSCy Schubert # After a full resync request, these lines could appear in 45*7f2fe78bSCy Schubert # either order. 46*7f2fe78bSCy Schubert if 'Waiting for' in line: 47*7f2fe78bSCy Schubert sleep_seen = True 48*7f2fe78bSCy Schubert if 'load process for full propagation completed' in line: 49*7f2fe78bSCy Schubert full_seen = True 50*7f2fe78bSCy Schubert 51*7f2fe78bSCy Schubert # Detect some failure conditions. 52*7f2fe78bSCy Schubert if 'Still waiting for full resync' in line: 53*7f2fe78bSCy Schubert fail('kadmind gave consecutive full resyncs') 54*7f2fe78bSCy Schubert if 'Rejected connection' in line: 55*7f2fe78bSCy Schubert fail('kpropd rejected kprop connection') 56*7f2fe78bSCy Schubert if 'get updates failed' in line: 57*7f2fe78bSCy Schubert fail('iprop_get_updates failed') 58*7f2fe78bSCy Schubert if 'permission denied' in line: 59*7f2fe78bSCy Schubert fail('kadmind denied update') 60*7f2fe78bSCy Schubert if ('error from primary' in line or 61*7f2fe78bSCy Schubert 'error returned from primary' in line): 62*7f2fe78bSCy Schubert fail('kadmind reported error') 63*7f2fe78bSCy Schubert if 'invalid return' in line: 64*7f2fe78bSCy Schubert fail('kadmind returned invalid result') 65*7f2fe78bSCy Schubert 66*7f2fe78bSCy Schubert if full_expected and not full_seen: 67*7f2fe78bSCy Schubert fail('Expected full dump but saw only incremental') 68*7f2fe78bSCy Schubert if full_seen and not full_expected: 69*7f2fe78bSCy Schubert fail('Expected incremental prop but saw full dump') 70*7f2fe78bSCy Schubert if old_sno != expected_old: 71*7f2fe78bSCy Schubert fail('Expected old serial %d from kpropd sync' % expected_old) 72*7f2fe78bSCy Schubert if new_sno != expected_new: 73*7f2fe78bSCy Schubert fail('Expected new serial %d from kpropd sync' % expected_new) 74*7f2fe78bSCy Schubert 75*7f2fe78bSCy Schubert # Wait until kpropd is sleeping before continuing, to avoid races. 76*7f2fe78bSCy Schubert # (This is imperfect since there's there is a short window between 77*7f2fe78bSCy Schubert # the fprintf and the sleep; kpropd will need design changes to 78*7f2fe78bSCy Schubert # fix that.) 79*7f2fe78bSCy Schubert while True: 80*7f2fe78bSCy Schubert line = kpropd.stdout.readline() 81*7f2fe78bSCy Schubert output('kpropd: ' + line) 82*7f2fe78bSCy Schubert if 'Waiting for' in line: 83*7f2fe78bSCy Schubert break 84*7f2fe78bSCy Schubert output('*** Sync complete\n') 85*7f2fe78bSCy Schubert 86*7f2fe78bSCy Schubert# Verify the output of kproplog against the expected number of 87*7f2fe78bSCy Schubert# entries, first and last serial number, and a list of principal names 88*7f2fe78bSCy Schubert# for the update entrires. 89*7f2fe78bSCy Schubertdef check_ulog(num, first, last, entries, env=None): 90*7f2fe78bSCy Schubert out = realm.run([kproplog], env=env) 91*7f2fe78bSCy Schubert if 'Number of entries : ' + str(num) + '\n' not in out: 92*7f2fe78bSCy Schubert fail('Expected %d entries' % num) 93*7f2fe78bSCy Schubert if last: 94*7f2fe78bSCy Schubert firststr = first and str(first) or 'None' 95*7f2fe78bSCy Schubert if 'First serial # : ' + firststr + '\n' not in out: 96*7f2fe78bSCy Schubert fail('Expected first serial number %d' % first) 97*7f2fe78bSCy Schubert laststr = last and str(last) or 'None' 98*7f2fe78bSCy Schubert if 'Last serial # : ' + laststr + '\n' not in out: 99*7f2fe78bSCy Schubert fail('Expected last serial number %d' % last) 100*7f2fe78bSCy Schubert assert(len(entries) == num) 101*7f2fe78bSCy Schubert ser = first - 1 102*7f2fe78bSCy Schubert entindex = 0 103*7f2fe78bSCy Schubert for line in out.splitlines(): 104*7f2fe78bSCy Schubert m = re.match(r'\tUpdate serial # : (\d+)$', line) 105*7f2fe78bSCy Schubert if m: 106*7f2fe78bSCy Schubert ser = ser + 1 107*7f2fe78bSCy Schubert if m.group(1) != str(ser): 108*7f2fe78bSCy Schubert fail('Expected serial number %d in update entry' % ser) 109*7f2fe78bSCy Schubert m = re.match(r'\tUpdate principal : (.*)$', line) 110*7f2fe78bSCy Schubert if m: 111*7f2fe78bSCy Schubert eprinc = entries[ser - first] 112*7f2fe78bSCy Schubert if eprinc == None: 113*7f2fe78bSCy Schubert fail('Expected dummy update entry %d' % ser) 114*7f2fe78bSCy Schubert elif m.group(1) != eprinc: 115*7f2fe78bSCy Schubert fail('Expected princ %s in update entry %d' % (eprinc, ser)) 116*7f2fe78bSCy Schubert if line == '\tDummy entry': 117*7f2fe78bSCy Schubert eprinc = entries[ser - first] 118*7f2fe78bSCy Schubert if eprinc != None: 119*7f2fe78bSCy Schubert fail('Expected princ %s in update entry %d' % (eprinc, ser)) 120*7f2fe78bSCy Schubert 121*7f2fe78bSCy Schubert# replica1 will receive updates from primary, and replica2 will 122*7f2fe78bSCy Schubert# receive updates from replica1. Because of the awkward way iprop and 123*7f2fe78bSCy Schubert# kprop port configuration currently works, we need separate config 124*7f2fe78bSCy Schubert# files for the replica and primary sides of replica1, but they use 125*7f2fe78bSCy Schubert# the same DB and ulog file. 126*7f2fe78bSCy Schubertconf = {'realms': {'$realm': {'iprop_enable': 'true', 127*7f2fe78bSCy Schubert 'iprop_logfile': '$testdir/db.ulog'}}} 128*7f2fe78bSCy Schubertconf_rep1 = {'realms': {'$realm': {'iprop_replica_poll': '600', 129*7f2fe78bSCy Schubert 'iprop_logfile': '$testdir/ulog.replica1'}}, 130*7f2fe78bSCy Schubert 'dbmodules': {'db': {'database_name': '$testdir/db.replica1'}}} 131*7f2fe78bSCy Schubertconf_rep1m = {'realms': {'$realm': {'iprop_logfile': '$testdir/ulog.replica1', 132*7f2fe78bSCy Schubert 'iprop_port': '$port8'}}, 133*7f2fe78bSCy Schubert 'dbmodules': {'db': {'database_name': '$testdir/db.replica1'}}} 134*7f2fe78bSCy Schubertconf_rep2 = {'realms': {'$realm': {'iprop_replica_poll': '600', 135*7f2fe78bSCy Schubert 'iprop_logfile': '$testdir/ulog.replica2', 136*7f2fe78bSCy Schubert 'iprop_port': '$port8'}}, 137*7f2fe78bSCy Schubert 'dbmodules': {'db': {'database_name': '$testdir/db.replica2'}}} 138*7f2fe78bSCy Schubert 139*7f2fe78bSCy Schubertconf_foo = {'libdefaults': {'default_realm': 'FOO'}, 140*7f2fe78bSCy Schubert 'domain_realm': {hostname: 'FOO'}} 141*7f2fe78bSCy Schubertconf_rep3 = {'realms': {'$realm': {'iprop_replica_poll': '600', 142*7f2fe78bSCy Schubert 'iprop_logfile': '$testdir/ulog.replica3', 143*7f2fe78bSCy Schubert 'iprop_port': '$port8'}, 144*7f2fe78bSCy Schubert 'FOO': {'iprop_logfile': '$testdir/ulog.replica3'}}, 145*7f2fe78bSCy Schubert 'dbmodules': {'db': {'database_name': '$testdir/db.replica3'}}} 146*7f2fe78bSCy Schubert 147*7f2fe78bSCy Schubertkrb5_conf_rep4 = {'domain_realm': {hostname: 'FOO'}} 148*7f2fe78bSCy Schubertconf_rep4 = {'realms': {'$realm': {'iprop_replica_poll': '600', 149*7f2fe78bSCy Schubert 'iprop_logfile': '$testdir/ulog.replica4', 150*7f2fe78bSCy Schubert 'iprop_port': '$port8'}}, 151*7f2fe78bSCy Schubert 'dbmodules': {'db': {'database_name': '$testdir/db.replica4'}}} 152*7f2fe78bSCy Schubert 153*7f2fe78bSCy Schubertfor realm in multidb_realms(kdc_conf=conf, create_user=False, 154*7f2fe78bSCy Schubert start_kadmind=True): 155*7f2fe78bSCy Schubert replica1 = realm.special_env('replica1', True, kdc_conf=conf_rep1) 156*7f2fe78bSCy Schubert replica1m = realm.special_env('replica1m', True, krb5_conf=conf_foo, 157*7f2fe78bSCy Schubert kdc_conf=conf_rep1m) 158*7f2fe78bSCy Schubert replica2 = realm.special_env('replica2', True, kdc_conf=conf_rep2) 159*7f2fe78bSCy Schubert 160*7f2fe78bSCy Schubert # A default_realm and domain_realm that do not match the KDC's 161*7f2fe78bSCy Schubert # realm. The FOO realm iprop_logfile setting is needed to run 162*7f2fe78bSCy Schubert # kproplog during a replica3 test, since kproplog has no realm 163*7f2fe78bSCy Schubert # option. 164*7f2fe78bSCy Schubert replica3 = realm.special_env('replica3', True, krb5_conf=conf_foo, 165*7f2fe78bSCy Schubert kdc_conf=conf_rep3) 166*7f2fe78bSCy Schubert 167*7f2fe78bSCy Schubert # A default realm and a domain realm map that differ. 168*7f2fe78bSCy Schubert replica4 = realm.special_env('replica4', True, krb5_conf=krb5_conf_rep4, 169*7f2fe78bSCy Schubert kdc_conf=conf_rep4) 170*7f2fe78bSCy Schubert 171*7f2fe78bSCy Schubert # Define some principal names. pr3 is long enough to cause internal 172*7f2fe78bSCy Schubert # reallocs, but not long enough to grow the basic ulog entry size. 173*7f2fe78bSCy Schubert pr1 = 'wakawaka@' + realm.realm 174*7f2fe78bSCy Schubert pr2 = 'w@' + realm.realm 175*7f2fe78bSCy Schubert c = 'chocolate-flavored-school-bus' 176*7f2fe78bSCy Schubert cs = c + '/' 177*7f2fe78bSCy Schubert pr3 = (cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + c + 178*7f2fe78bSCy Schubert '@' + realm.realm) 179*7f2fe78bSCy Schubert 180*7f2fe78bSCy Schubert # Create the kpropd ACL file. 181*7f2fe78bSCy Schubert acl_file = os.path.join(realm.testdir, 'kpropd-acl') 182*7f2fe78bSCy Schubert acl = open(acl_file, 'w') 183*7f2fe78bSCy Schubert acl.write(realm.host_princ + '\n') 184*7f2fe78bSCy Schubert acl.close() 185*7f2fe78bSCy Schubert 186*7f2fe78bSCy Schubert ulog = os.path.join(realm.testdir, 'db.ulog') 187*7f2fe78bSCy Schubert if not os.path.exists(ulog): 188*7f2fe78bSCy Schubert fail('update log not created: ' + ulog) 189*7f2fe78bSCy Schubert 190*7f2fe78bSCy Schubert # Create the principal used to authenticate kpropd to kadmind. 191*7f2fe78bSCy Schubert kiprop_princ = 'kiprop/' + hostname 192*7f2fe78bSCy Schubert realm.addprinc(kiprop_princ) 193*7f2fe78bSCy Schubert realm.extract_keytab(kiprop_princ, realm.keytab) 194*7f2fe78bSCy Schubert 195*7f2fe78bSCy Schubert # Create the initial replica databases. 196*7f2fe78bSCy Schubert dumpfile = os.path.join(realm.testdir, 'dump') 197*7f2fe78bSCy Schubert realm.run([kdb5_util, 'dump', dumpfile]) 198*7f2fe78bSCy Schubert realm.run([kdb5_util, 'load', dumpfile], replica1) 199*7f2fe78bSCy Schubert realm.run([kdb5_util, 'load', dumpfile], replica2) 200*7f2fe78bSCy Schubert realm.run([kdb5_util, '-r', realm.realm, 'load', dumpfile], replica3) 201*7f2fe78bSCy Schubert realm.run([kdb5_util, 'load', dumpfile], replica4) 202*7f2fe78bSCy Schubert 203*7f2fe78bSCy Schubert # Reinitialize the primary ulog so we know exactly what to expect 204*7f2fe78bSCy Schubert # in it. 205*7f2fe78bSCy Schubert realm.run([kproplog, '-R']) 206*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None]) 207*7f2fe78bSCy Schubert 208*7f2fe78bSCy Schubert # Make some changes to the primary DB. 209*7f2fe78bSCy Schubert realm.addprinc(pr1) 210*7f2fe78bSCy Schubert realm.addprinc(pr3) 211*7f2fe78bSCy Schubert realm.addprinc(pr2) 212*7f2fe78bSCy Schubert realm.run([kadminl, 'modprinc', '-allow_tix', pr2]) 213*7f2fe78bSCy Schubert realm.run([kadminl, 'modprinc', '+allow_tix', pr2]) 214*7f2fe78bSCy Schubert check_ulog(6, 1, 6, [None, pr1, pr3, pr2, pr2, pr2]) 215*7f2fe78bSCy Schubert 216*7f2fe78bSCy Schubert # Start kpropd for replica1 and get a full dump from primary. 217*7f2fe78bSCy Schubert mark('propagate M->1 full') 218*7f2fe78bSCy Schubert kpropd1 = realm.start_kpropd(replica1, ['-d']) 219*7f2fe78bSCy Schubert wait_for_prop(kpropd1, True, 1, 6) 220*7f2fe78bSCy Schubert out = realm.run([kadminl, 'listprincs'], env=replica1) 221*7f2fe78bSCy Schubert if pr1 not in out or pr2 not in out or pr3 not in out: 222*7f2fe78bSCy Schubert fail('replica1 does not have all principals from primary') 223*7f2fe78bSCy Schubert check_ulog(1, 6, 6, [None], replica1) 224*7f2fe78bSCy Schubert 225*7f2fe78bSCy Schubert # Make a change and check that it propagates incrementally. 226*7f2fe78bSCy Schubert mark('propagate M->1 incremental') 227*7f2fe78bSCy Schubert realm.run([kadminl, 'modprinc', '-allow_tix', pr2]) 228*7f2fe78bSCy Schubert check_ulog(7, 1, 7, [None, pr1, pr3, pr2, pr2, pr2, pr2]) 229*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 230*7f2fe78bSCy Schubert wait_for_prop(kpropd1, False, 6, 7) 231*7f2fe78bSCy Schubert check_ulog(2, 6, 7, [None, pr2], replica1) 232*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr2], env=replica1, 233*7f2fe78bSCy Schubert expected_msg='Attributes: DISALLOW_ALL_TIX') 234*7f2fe78bSCy Schubert 235*7f2fe78bSCy Schubert # Start kadmind -proponly for replica1. (Use the replica1m 236*7f2fe78bSCy Schubert # environment which defines iprop_port to $port8.) 237*7f2fe78bSCy Schubert replica1_out_dump_path = os.path.join(realm.testdir, 'dump.replica1.out') 238*7f2fe78bSCy Schubert replica2_in_dump_path = os.path.join(realm.testdir, 'dump.replica2.in') 239*7f2fe78bSCy Schubert replica2_kprop_port = str(realm.portbase + 9) 240*7f2fe78bSCy Schubert kadmind_proponly = realm.start_server([kadmind, '-r', realm.realm, 241*7f2fe78bSCy Schubert '-nofork', '-proponly', 242*7f2fe78bSCy Schubert '-p', kdb5_util, 243*7f2fe78bSCy Schubert '-K', kprop, '-k', 244*7f2fe78bSCy Schubert replica2_kprop_port, 245*7f2fe78bSCy Schubert '-F', replica1_out_dump_path], 246*7f2fe78bSCy Schubert 'starting...', replica1m) 247*7f2fe78bSCy Schubert 248*7f2fe78bSCy Schubert # Test similar default_realm and domain_realm map settings with -r realm. 249*7f2fe78bSCy Schubert mark('propagate 1->3 full') 250*7f2fe78bSCy Schubert replica3_in_dump_path = os.path.join(realm.testdir, 'dump.replica3.in') 251*7f2fe78bSCy Schubert kpropd3 = realm.start_server([kpropd, '-d', '-D', '-r', realm.realm, '-P', 252*7f2fe78bSCy Schubert replica2_kprop_port, '-f', 253*7f2fe78bSCy Schubert replica3_in_dump_path, '-p', kdb5_util, '-a', 254*7f2fe78bSCy Schubert acl_file, '-A', hostname], 'ready', replica3) 255*7f2fe78bSCy Schubert wait_for_prop(kpropd3, True, 1, 7) 256*7f2fe78bSCy Schubert out = realm.run([kadminl, '-r', realm.realm, 'listprincs'], env=replica3) 257*7f2fe78bSCy Schubert if pr1 not in out or pr2 not in out or pr3 not in out: 258*7f2fe78bSCy Schubert fail('replica3 does not have all principals from replica1') 259*7f2fe78bSCy Schubert check_ulog(1, 7, 7, [None], env=replica3) 260*7f2fe78bSCy Schubert 261*7f2fe78bSCy Schubert # Test an incremental propagation for the kpropd -r case. 262*7f2fe78bSCy Schubert mark('propagate M->1->3 incremental') 263*7f2fe78bSCy Schubert realm.run([kadminl, 'modprinc', '-maxlife', '20 minutes', pr1]) 264*7f2fe78bSCy Schubert check_ulog(8, 1, 8, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1]) 265*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 266*7f2fe78bSCy Schubert wait_for_prop(kpropd1, False, 7, 8) 267*7f2fe78bSCy Schubert check_ulog(3, 6, 8, [None, pr2, pr1], replica1) 268*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr1], env=replica1, 269*7f2fe78bSCy Schubert expected_msg='Maximum ticket life: 0 days 00:20:00') 270*7f2fe78bSCy Schubert kpropd3.send_signal(signal.SIGUSR1) 271*7f2fe78bSCy Schubert wait_for_prop(kpropd3, False, 7, 8) 272*7f2fe78bSCy Schubert check_ulog(2, 7, 8, [None, pr1], replica3) 273*7f2fe78bSCy Schubert realm.run([kadminl, '-r', realm.realm, 'getprinc', pr1], env=replica3, 274*7f2fe78bSCy Schubert expected_msg='Maximum ticket life: 0 days 00:20:00') 275*7f2fe78bSCy Schubert stop_daemon(kpropd3) 276*7f2fe78bSCy Schubert 277*7f2fe78bSCy Schubert # Test dissimilar default_realm and domain_realm map settings (no 278*7f2fe78bSCy Schubert # -r realm). 279*7f2fe78bSCy Schubert mark('propagate 1->4 full') 280*7f2fe78bSCy Schubert replica4_in_dump_path = os.path.join(realm.testdir, 'dump.replica4.in') 281*7f2fe78bSCy Schubert kpropd4 = realm.start_server([kpropd, '-d', '-D', '-P', 282*7f2fe78bSCy Schubert replica2_kprop_port, '-f', 283*7f2fe78bSCy Schubert replica4_in_dump_path, '-p', kdb5_util, 284*7f2fe78bSCy Schubert '-a', acl_file, '-A', hostname], 'ready', 285*7f2fe78bSCy Schubert replica4) 286*7f2fe78bSCy Schubert wait_for_prop(kpropd4, True, 1, 8) 287*7f2fe78bSCy Schubert out = realm.run([kadminl, 'listprincs'], env=replica4) 288*7f2fe78bSCy Schubert if pr1 not in out or pr2 not in out or pr3 not in out: 289*7f2fe78bSCy Schubert fail('replica4 does not have all principals from replica1') 290*7f2fe78bSCy Schubert stop_daemon(kpropd4) 291*7f2fe78bSCy Schubert 292*7f2fe78bSCy Schubert # Start kpropd for replica2. The -A option isn't needed since 293*7f2fe78bSCy Schubert # we're talking to the same host as primary (we specify it anyway 294*7f2fe78bSCy Schubert # to exercise the code), but replica2 defines iprop_port to $port8 295*7f2fe78bSCy Schubert # so it will talk to replica1. Get a full dump from replica1. 296*7f2fe78bSCy Schubert mark('propagate 1->2 full') 297*7f2fe78bSCy Schubert kpropd2 = realm.start_server([kpropd, '-d', '-D', '-P', 298*7f2fe78bSCy Schubert replica2_kprop_port, '-f', 299*7f2fe78bSCy Schubert replica2_in_dump_path, '-p', kdb5_util, 300*7f2fe78bSCy Schubert '-a', acl_file, '-A', hostname], 'ready', 301*7f2fe78bSCy Schubert replica2) 302*7f2fe78bSCy Schubert wait_for_prop(kpropd2, True, 1, 8) 303*7f2fe78bSCy Schubert check_ulog(2, 7, 8, [None, pr1], replica2) 304*7f2fe78bSCy Schubert out = realm.run([kadminl, 'listprincs'], env=replica1) 305*7f2fe78bSCy Schubert if pr1 not in out or pr2 not in out or pr3 not in out: 306*7f2fe78bSCy Schubert fail('replica2 does not have all principals from replica1') 307*7f2fe78bSCy Schubert 308*7f2fe78bSCy Schubert # Make another change and check that it propagates incrementally 309*7f2fe78bSCy Schubert # to both replicas. 310*7f2fe78bSCy Schubert mark('propagate M->1->2 incremental') 311*7f2fe78bSCy Schubert realm.run([kadminl, 'modprinc', '-maxrenewlife', '22 hours', pr1]) 312*7f2fe78bSCy Schubert check_ulog(9, 1, 9, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1, pr1]) 313*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 314*7f2fe78bSCy Schubert wait_for_prop(kpropd1, False, 8, 9) 315*7f2fe78bSCy Schubert check_ulog(4, 6, 9, [None, pr2, pr1, pr1], replica1) 316*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr1], env=replica1, 317*7f2fe78bSCy Schubert expected_msg='Maximum renewable life: 0 days 22:00:00\n') 318*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 319*7f2fe78bSCy Schubert wait_for_prop(kpropd2, False, 8, 9) 320*7f2fe78bSCy Schubert check_ulog(3, 7, 9, [None, pr1, pr1], replica2) 321*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr1], env=replica2, 322*7f2fe78bSCy Schubert expected_msg='Maximum renewable life: 0 days 22:00:00\n') 323*7f2fe78bSCy Schubert 324*7f2fe78bSCy Schubert # Reset the ulog on replica1 to force a full resync from primary. 325*7f2fe78bSCy Schubert # The resync will use the old dump file and then propagate 326*7f2fe78bSCy Schubert # changes. replica2 should still be in sync with replica1 after 327*7f2fe78bSCy Schubert # the resync, so make sure it doesn't take a full resync. 328*7f2fe78bSCy Schubert mark('propagate M->1->2 full') 329*7f2fe78bSCy Schubert realm.run([kproplog, '-R'], replica1) 330*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica1) 331*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 332*7f2fe78bSCy Schubert wait_for_prop(kpropd1, True, 1, 9) 333*7f2fe78bSCy Schubert check_ulog(4, 6, 9, [None, pr2, pr1, pr1], replica1) 334*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 335*7f2fe78bSCy Schubert wait_for_prop(kpropd2, False, 9, 9) 336*7f2fe78bSCy Schubert check_ulog(3, 7, 9, [None, pr1, pr1], replica2) 337*7f2fe78bSCy Schubert 338*7f2fe78bSCy Schubert # Make another change and check that it propagates incrementally to 339*7f2fe78bSCy Schubert # both replicas. 340*7f2fe78bSCy Schubert mark('propagate M->1->2 incremental (after reset)') 341*7f2fe78bSCy Schubert realm.run([kadminl, 'modprinc', '+allow_tix', pr2]) 342*7f2fe78bSCy Schubert check_ulog(10, 1, 10, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1, pr1, pr2]) 343*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 344*7f2fe78bSCy Schubert wait_for_prop(kpropd1, False, 9, 10) 345*7f2fe78bSCy Schubert check_ulog(5, 6, 10, [None, pr2, pr1, pr1, pr2], replica1) 346*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr2], env=replica1, 347*7f2fe78bSCy Schubert expected_msg='Attributes:\n') 348*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 349*7f2fe78bSCy Schubert wait_for_prop(kpropd2, False, 9, 10) 350*7f2fe78bSCy Schubert check_ulog(4, 7, 10, [None, pr1, pr1, pr2], replica2) 351*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr2], env=replica2, 352*7f2fe78bSCy Schubert expected_msg='Attributes:\n') 353*7f2fe78bSCy Schubert 354*7f2fe78bSCy Schubert # Create a policy and check that it propagates via full resync. 355*7f2fe78bSCy Schubert mark('propagate M->1->2 full (new policy)') 356*7f2fe78bSCy Schubert realm.run([kadminl, 'addpol', '-minclasses', '2', 'testpol']) 357*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None]) 358*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 359*7f2fe78bSCy Schubert wait_for_prop(kpropd1, True, 10, 1) 360*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica1) 361*7f2fe78bSCy Schubert realm.run([kadminl, 'getpol', 'testpol'], env=replica1, 362*7f2fe78bSCy Schubert expected_msg='Minimum number of password character classes: 2') 363*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 364*7f2fe78bSCy Schubert wait_for_prop(kpropd2, True, 10, 1) 365*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica2) 366*7f2fe78bSCy Schubert realm.run([kadminl, 'getpol', 'testpol'], env=replica2, 367*7f2fe78bSCy Schubert expected_msg='Minimum number of password character classes: 2') 368*7f2fe78bSCy Schubert 369*7f2fe78bSCy Schubert # Modify the policy and test that it also propagates via full resync. 370*7f2fe78bSCy Schubert mark('propagate M->1->2 full (policy change)') 371*7f2fe78bSCy Schubert realm.run([kadminl, 'modpol', '-minlength', '17', 'testpol']) 372*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None]) 373*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 374*7f2fe78bSCy Schubert wait_for_prop(kpropd1, True, 1, 1) 375*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica1) 376*7f2fe78bSCy Schubert realm.run([kadminl, 'getpol', 'testpol'], env=replica1, 377*7f2fe78bSCy Schubert expected_msg='Minimum password length: 17') 378*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 379*7f2fe78bSCy Schubert wait_for_prop(kpropd2, True, 1, 1) 380*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica2) 381*7f2fe78bSCy Schubert realm.run([kadminl, 'getpol', 'testpol'], env=replica2, 382*7f2fe78bSCy Schubert expected_msg='Minimum password length: 17') 383*7f2fe78bSCy Schubert 384*7f2fe78bSCy Schubert # Delete the policy and test that it propagates via full resync. 385*7f2fe78bSCy Schubert mark('propgate M->1->2 full (policy delete)') 386*7f2fe78bSCy Schubert realm.run([kadminl, 'delpol', 'testpol']) 387*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None]) 388*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 389*7f2fe78bSCy Schubert wait_for_prop(kpropd1, True, 1, 1) 390*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica1) 391*7f2fe78bSCy Schubert realm.run([kadminl, 'getpol', 'testpol'], env=replica1, expected_code=1, 392*7f2fe78bSCy Schubert expected_msg='Policy does not exist') 393*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 394*7f2fe78bSCy Schubert wait_for_prop(kpropd2, True, 1, 1) 395*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica2) 396*7f2fe78bSCy Schubert realm.run([kadminl, 'getpol', 'testpol'], env=replica2, expected_code=1, 397*7f2fe78bSCy Schubert expected_msg='Policy does not exist') 398*7f2fe78bSCy Schubert 399*7f2fe78bSCy Schubert # Modify a principal on the primary and test that it propagates 400*7f2fe78bSCy Schubert # incrementally. 401*7f2fe78bSCy Schubert mark('propagate M->1->2 incremental (after policy changes)') 402*7f2fe78bSCy Schubert realm.run([kadminl, 'modprinc', '-maxlife', '10 minutes', pr1]) 403*7f2fe78bSCy Schubert check_ulog(2, 1, 2, [None, pr1]) 404*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 405*7f2fe78bSCy Schubert wait_for_prop(kpropd1, False, 1, 2) 406*7f2fe78bSCy Schubert check_ulog(2, 1, 2, [None, pr1], replica1) 407*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr1], env=replica1, 408*7f2fe78bSCy Schubert expected_msg='Maximum ticket life: 0 days 00:10:00') 409*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 410*7f2fe78bSCy Schubert wait_for_prop(kpropd2, False, 1, 2) 411*7f2fe78bSCy Schubert check_ulog(2, 1, 2, [None, pr1], replica2) 412*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr1], env=replica2, 413*7f2fe78bSCy Schubert expected_msg='Maximum ticket life: 0 days 00:10:00') 414*7f2fe78bSCy Schubert 415*7f2fe78bSCy Schubert # Delete a principal and test that it propagates incrementally. 416*7f2fe78bSCy Schubert mark('propagate M->1->2 incremental (princ delete)') 417*7f2fe78bSCy Schubert realm.run([kadminl, 'delprinc', pr3]) 418*7f2fe78bSCy Schubert check_ulog(3, 1, 3, [None, pr1, pr3]) 419*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 420*7f2fe78bSCy Schubert wait_for_prop(kpropd1, False, 2, 3) 421*7f2fe78bSCy Schubert check_ulog(3, 1, 3, [None, pr1, pr3], replica1) 422*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr3], env=replica1, expected_code=1, 423*7f2fe78bSCy Schubert expected_msg='Principal does not exist') 424*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 425*7f2fe78bSCy Schubert wait_for_prop(kpropd2, False, 2, 3) 426*7f2fe78bSCy Schubert check_ulog(3, 1, 3, [None, pr1, pr3], replica2) 427*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr3], env=replica2, expected_code=1, 428*7f2fe78bSCy Schubert expected_msg='Principal does not exist') 429*7f2fe78bSCy Schubert 430*7f2fe78bSCy Schubert # Rename a principal and test that it propagates incrementally. 431*7f2fe78bSCy Schubert mark('propagate M->1->2 incremental (princ rename)') 432*7f2fe78bSCy Schubert renpr = "quacked@" + realm.realm 433*7f2fe78bSCy Schubert realm.run([kadminl, 'renprinc', pr1, renpr]) 434*7f2fe78bSCy Schubert check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr]) 435*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 436*7f2fe78bSCy Schubert wait_for_prop(kpropd1, False, 3, 6) 437*7f2fe78bSCy Schubert check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr], replica1) 438*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr1], env=replica1, expected_code=1, 439*7f2fe78bSCy Schubert expected_msg='Principal does not exist') 440*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', renpr], env=replica1) 441*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 442*7f2fe78bSCy Schubert wait_for_prop(kpropd2, False, 3, 6) 443*7f2fe78bSCy Schubert check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr], replica2) 444*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr1], env=replica2, expected_code=1, 445*7f2fe78bSCy Schubert expected_msg='Principal does not exist') 446*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', renpr], env=replica2) 447*7f2fe78bSCy Schubert 448*7f2fe78bSCy Schubert pr1 = renpr 449*7f2fe78bSCy Schubert 450*7f2fe78bSCy Schubert # Reset the ulog on the primary to force a full resync. 451*7f2fe78bSCy Schubert mark('propagate M->1->2 full (ulog reset)') 452*7f2fe78bSCy Schubert realm.run([kproplog, '-R']) 453*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None]) 454*7f2fe78bSCy Schubert kpropd1.send_signal(signal.SIGUSR1) 455*7f2fe78bSCy Schubert wait_for_prop(kpropd1, True, 6, 1) 456*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica1) 457*7f2fe78bSCy Schubert kpropd2.send_signal(signal.SIGUSR1) 458*7f2fe78bSCy Schubert wait_for_prop(kpropd2, True, 6, 1) 459*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica2) 460*7f2fe78bSCy Schubert 461*7f2fe78bSCy Schubert # Stop the kprop daemons so we can test kpropd -t. 462*7f2fe78bSCy Schubert realm.stop_kpropd(kpropd1) 463*7f2fe78bSCy Schubert stop_daemon(kpropd2) 464*7f2fe78bSCy Schubert stop_daemon(kadmind_proponly) 465*7f2fe78bSCy Schubert mark('kpropd -t') 466*7f2fe78bSCy Schubert 467*7f2fe78bSCy Schubert # Test the case where no updates are needed. 468*7f2fe78bSCy Schubert out = realm.run_kpropd_once(replica1, ['-d']) 469*7f2fe78bSCy Schubert if 'KDC is synchronized' not in out: 470*7f2fe78bSCy Schubert fail('Expected synchronized from kpropd -t') 471*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica1) 472*7f2fe78bSCy Schubert 473*7f2fe78bSCy Schubert # Make a change on the primary and fetch it incrementally. 474*7f2fe78bSCy Schubert realm.run([kadminl, 'modprinc', '-maxlife', '5 minutes', pr1]) 475*7f2fe78bSCy Schubert check_ulog(2, 1, 2, [None, pr1]) 476*7f2fe78bSCy Schubert out = realm.run_kpropd_once(replica1, ['-d']) 477*7f2fe78bSCy Schubert if 'Got incremental updates (sno=2 ' not in out: 478*7f2fe78bSCy Schubert fail('Expected full dump and synchronized from kpropd -t') 479*7f2fe78bSCy Schubert check_ulog(2, 1, 2, [None, pr1], replica1) 480*7f2fe78bSCy Schubert realm.run([kadminl, 'getprinc', pr1], env=replica1, 481*7f2fe78bSCy Schubert expected_msg='Maximum ticket life: 0 days 00:05:00') 482*7f2fe78bSCy Schubert 483*7f2fe78bSCy Schubert # Propagate a policy change via full resync. 484*7f2fe78bSCy Schubert realm.run([kadminl, 'addpol', '-minclasses', '3', 'testpol']) 485*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None]) 486*7f2fe78bSCy Schubert out = realm.run_kpropd_once(replica1, ['-d']) 487*7f2fe78bSCy Schubert if ('Full propagation transfer finished' not in out or 488*7f2fe78bSCy Schubert 'KDC is synchronized' not in out): 489*7f2fe78bSCy Schubert fail('Expected full dump and synchronized from kpropd -t') 490*7f2fe78bSCy Schubert check_ulog(1, 1, 1, [None], replica1) 491*7f2fe78bSCy Schubert realm.run([kadminl, 'getpol', 'testpol'], env=replica1, 492*7f2fe78bSCy Schubert expected_msg='Minimum number of password character classes: 3') 493*7f2fe78bSCy Schubert 494*7f2fe78bSCy Schubertsuccess('iprop tests') 495