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